본문 바로가기
공부/springboot

(1) 스프링 사용하지 않고 OCP, DIP 지키기

by 샤샤샤샤 2023. 5. 10.

아래의 코드는 Alphabet 이라는 인터페이스와 그를 상속받는 A, B의 클래스다. 여기서 className 이라는 메서드는 각각 자신의 클래스 이름 A, B를 출력하게 된다.

 

# 인터페이스

public interface Alphabet {
    void className();
}

#  구현체 A

public class A implements Alphabet{

    @Override
    public void className() {
        System.out.println("A");
    }
}

# 구현체 B

public class B implements Alphabet{
    @Override
    public void className() {
        System.out.println("B");
    }
}

이때 인터페이스 Alphabet을 참조하면서  A에서 구현된 className함수를 실행하려고 한다고 해보자.

OCP원칙을 지키지 않으면 아래와 같이 나올 것이다.

public class Controller {

    private final Alphabet alphabet = new A();  // 다형성을 이용한 필드값 설정

    public void mainClassName (){ //className함수 실행
        alphabet.className();
    }
    
    public static void main(String[] args) { // 실행함수
       Controller controller = new Controller();
       controller.mainClassName();
    }
}
  // 출력값: A

 언뜻 보면 위의 코드는 인터페이스에 의존하고 있으니 DIP원칙을 지키고 있는듯 보인다. 그러나 실제로는 Alphabet 인터페이스가 A라는 클래스에 의존하고 있어 결과적으로는 A라는 구현체에 직접적으로 의존하고 있다고 봐야 한다. 

 결국 인터페이스가 아닌 구현체에 의존적일뿐만 아니라, className구현체를 B로 바꾸기 위해서는 직접적으로 코드를 변

경해야만 하니 OCP역시 위반이다. 이런 문제를 해결하기 위해서는 중간 다리 역할을 해주는 Config 클래스가 하나 필요하다.

** 의존이란? 참조하고 있다는 의미정도로 이해하면 편하다.

public class Config {
    public Alphabet alphabet(){
        return new A();
    }
}

해당 클래스는 Alphabet타입으로 A객체를 반환하는 메서드를 가지고 있다. 이를 활용하면 Controller클래스를 아래와 같이 수정 가능하다.

public class Controller {

    private final Alphabet alphabet;  // 오직 인터페이스만 의존

    public Controller(Alphabet alphabet) { // 인터페이스를 생성자 함수로 주입받음
        this.alphabet = alphabet;
    }
    
    public void mainClassName (){
        alphabet.className();
    }

    public static void main(String[] args) {
        Config config = new Config();  // Config객체 생성
        Alphabet instance = config.alphabet(); // Config의 alphabetA함수로 만들어진 객체 instance
        Controller controller = new Controller(instance); // Alphabet타입의 구현체를 주입
        controller.mainClassName();
    }
}
// 출력값 : A

위의 코드를 그림으로 나타내면 아래와 같다.

이 코드의 경우, Controller 객체는 분명하게 인터페이스에 의존하고 있다. 또한 구현체 A를 B로 바꾸고 싶다면 Config 함수에서 의존하고 있는 구현체를 B로 교환하기만 하면 된다. 

public class Config {
    public Alphabet alphabet(){
        return new B();
    }
}

이경우 출력값이 B로 변한다.

 

스프링을 사용 않고 객체지향의 원칙을 지키기 위해서는 이런 복잡한 과정이 필요하다.

 

** 결국 Config 클래스의 코드는 여전히 구현체에 의존하고 있으며, 코드를 변경해야 하니 객체지향이 아니라고 생각할수도 있다. 그러나 다시 생각해보면, 모든 클래스가 완벽하게 객체지향의 원칙을 지키기란 코드 본질적으로 불가능하다는 것을 알수 있다. 따라서 클라이언트 코드(중심 코드)를 제어하기 위한 설정 파일은 좋은 객체지향을 만들기 위해 어쩔수 없이 만들어지는 일종의 예외 코드라고 이해해야 한다.

 

'공부 > springboot' 카테고리의 다른 글

(3) 스프링 컨테이너와 빈 조회  (0) 2023.05.10
(2) 스프링을 활용한 DI  (0) 2023.05.10
좋은 객체지향이란?  (0) 2023.05.10
스프링이란?  (0) 2023.05.10
스프링부트: 시큐리티(1)  (0) 2023.02.20