SOLID를 철저하게 지켰음에도 불구하고 OCP, DIP를 위반하는 일들이 종종 존재한다.
예를 들어
public class OrderServiceImpl implements OrderService {
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}
다음과 같이 역할과 구현을 구분했음에도 불구하고 기능을 확장, 수정하려면 다음과 같이 수행 코드를 변경해야 하는 일이 발생한다.
이 이유는 OrderServiceImpl가 discountPolicy 인터페이스를 의존하고 있지만, 동시에 RateDiscountPolicy, FixDiscountPolicy 즉 구현 클래스도 의존하고 있기 때문이다.
그렇다면 우리는 기능이 변경될 때마다 수많은 클래스를 변경해야 하는 문제를 만나게 될 수 있다.
해결책은 다른 무언가를 통해 discountPolicy의 구현체를 인젝션 받으면 된다.
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
public class AppConfig {
public MemoryMemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public DiscountPolicy discountPolicy() {
// 사용영역의 코드 수정없이 기능을 확장, 변경할 수 있게 되었다.
return new RateDiscountPolicy();
}
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
}
다음과 같이 AppConfig를 통해 주입을 받게 하면 된다.
이렇게 하면, 기능을 변경할 때 마다 OrderServiceImpl클래스 변경 없이 AppConfig만 변경해주면 되므로 객체지향의 원칙이 잘 지켜지게 된다.
즉, 사용 영역의 코드가 아닌 구성영역의 코드만 변경해주면 된다.
이렇게 인젝션 해주는 AppConfig같은 부분을 IoC컨테이너 또는 D.I 컨테이너 라고 한다.