Question
- 다형성이란?
- OCP란?
- DIP란?
- 다형성을 지키지만 OCP를 지키지 못하는 경우는 무엇이 있는가?
- 다형성을 지키면서 OCP도 지키기 위해서는 어떻게 해야 하는가?
- IoC란?
- DI란?
다형성이란?
- 하나의 인터페이스, 하나의 부모를 여러 하위 클래스들이 구현/상속하여 서로 다른 동작을 수행할 수 있음을 의미
- 상속, 오버라이딩, 오버로딩 등의 기능을 통해 기존 기능에 추가로 새로운 기능을 더할 수 있어 코드 중복은 줄이고 코드 재활용성은 증대
- 인터페이스와 구현체가 각각 존재하기 때문에 의존성은 인터페이스에 두고 실제 변경은 구현체에서 일어나 변경과 확장에 용이
OCP란?
- Open/Closed Principle
- 확장에는 열려 있으나 변경에는 닫혀 있어야 함
- 기능을 자유자재로 확장을 해야 하지만 코어 로직에 변경은 없어야 함
- 다형성에서 인터페이스와 구현체가 각각 분리된 부분을 활용
DIP란?
- Dependency Inversion Principle
- 추상체에 의존하고, 구현체에 의존하면 안된다는 원칙.
- 이 또한 다형성에서 인터페이스와 구현체가 각각 분리된 부분을 활용
다형성을 지키지만 OCP를 지키지 못하는 경우는 무엇이 있는가?
//private final Repository repository = new MemoryRepository();
private final Repository repository = new DBRepository();
- 위 코드는 기존에 Memory를 활용한 Repository를 사용하다가 DB를 사용한 Repository로 구현체를 변경한 케이스
- 로직인 기본적으로 Interface인 Repository에 의존하고 있고 구현체인 MemoryRepository, DBRepository에는 의존성이 없음.
- 따라서, 다형성은 잘 지치고 있다고 볼 수 있음.
- 하지만 OCP 원칙인 확장시 코어 로직에 변경이 없어야 하는데 위와 같이 결국 코어 로직을 변경하는 부분이 발생함.
- 이는 다형성은 잘 지켰지만 OCP는 지켰다고 볼 수 없음.
다형성을 지키면서 OCP도 지키기 위해서는 어떻게 해야 하는가?
public class AppConfig {
public Repository repository() {
return new DBRepository();
}
}
private final Repository repository = appConfig.repository();
- 객체를 직접 생성하는 것이 아니라 객체를 주입받는 식으로 변경하면 코어 로직이 변경되지 않음.
- 예를 들어서, 기존에는 코어 로직에서 new DBRepository와 같이 직접 구현체의 코드가 들어갔었다고 하면, 위의 코드에는 코어 로직에는 appConfig.repository()와 같이 불러오는 코드만 있고 DBRepository와 같은 구현체 코드는 존재하지 않음.
- 즉, 코어 로직은 인터페이스인 Repository에만 의존을 하고 있고 구현체와는 전혀 관련이 없음.
- 대신, 구현체에 대한 관리는 AppConfig와 같은 설정파일 관리용 별도의 클래스파일에서 관리함.
- 그리고 AppConfig와 같인 클래스가 실제 구현체인 DBRepository를 코어 로직의 repository에 주입해줌.
- 이 경우, 구현체인 DBRepository가 MemoryRepository와 같은 구현체로 변경되어도 기존 코어 로직에는 아무런 변경점은 주지 않고 AppConfig만 수정해주면 됨.
IoC란?
- Inversion of Control
- 제어의 역전이라는 뜻으로, 객체의 생성 및 흐름 제어를 개발자가 아닌 프레임워크(Spring 등)이 담당한다는 개념
- 에를 들어, private final Repository repository = new DBRepository() 와 같은 코드는 개발자가 직접 객체의 생성을 제어하고 컴파일 시점에 적용이 되는 부분임.
- 하지만 제어의 역전이 적용된 경우 객체 생성부터 제거될 때 까지의 라이프 사이클을 프레임워크가 관리하게 됨.
- 예를 들어, 위 코드에서는 AppConfig에서 동적으로 생성되어서 저장되며, 스프링에서는 이를 위한 스프링컨테이너가 존재함.
DI란?
- Dependency Injection
- 의존성을 주입한다는 의미로, 정적인 상태에서는 의존성이 없고 런타임 시점에서 실제 구현 객체를 생성하고 클라이언트에 전달해 의존성을 넣어줌.
- 코어 로직만 봐서는 인터페이스에 어떤 구현체가 사용될지 알 수는 없으나 런타임 상황에서 의존성이 주입됨.
- 위 코드에서 Repository 구현체에는 무엇이 사용될지 알 수 없지만 실제 런타임시 AppConfig에서 DBRepository를 주입해줌.