구현객체를 생성하고 연결하는 책임을 가지는 설정 클래스이다.
AppConfig는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해서 주입(연결) 해준다.
관심사의 분리: 객체를 생성하고 연결하는 역할과 실행하는 역할이 분리됐다.
public class AppConfig {//애플리케이션 전체를 설정하고 구성한다.
public MemberService memberService(){
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(new MemoryMemberRepository(),new FixDiscountPolicy());
}
}
MemberServiceImpl 입장에서 생성자를 통해 어떤 구현 객체가 주입될지는 알 수 없다.
MemberServcieImpl의 생성자를 통해서 어떤 구현 객체를 주입할지는 오로지 AppConfig에서 결정된다.
MemberServcieImpl은 의존관계에 대한 고민은 AppConfig에 맡기고 실행에만 집중하면 된다.
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
역할을 드러나게 하는 것이 중요하다. 역할이 나오고 그에 대한 구현이 어떻게 되는지 한눈에 확인 가능하다.
//변경전
public class AppConfig {
public MemberService memberService(){
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(new MemoryMemberRepository(),new FixDiscountPolicy());
}
}
//변경후
public class AppConfig {//애플리케이션 전체를 설정하고 구성한다.
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
private MemoryMemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
private FixDiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
}
고정 할인 정책에서 정률 할인 정책으로 변경할 시에는 구성 영역인 AppConfig 코드만 변경하면 된다. 사용 영역인 OrderServiceImpl의 코드는 전혀 변경할 필요가 없다.
public class AppConfig {//애플리케이션 전체를 설정하고 구성한다.
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
private MemoryMemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
private DiscountPolicy discountPolicy() {
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
정률 할인 정책을 적용해야 하는 상황에서 클라이언트 코드인 OrderService 구현체도 함께 변경해야 했다. 주문 서비스 클라이언트가 인터페이스인 DiscountPolicy뿐만 아니라 구체 클래스인 FixDiscountPolicy도 함께 의존했기 때문에 DIP를 위반했다.
OCP(Open-closed Principle)과 DIP(Dependency Injection Principle)을 만족한다. OCP는 확장에는 열려있고 변경에는 닫혀있는 원칙인데, 인터페이스를 통한 구체 개발(확장)은 가능하고 각각의 구현체를 수정하지 않아도 되기 때문에 변경에는 닫혀있는 것을 만족한다. DIP는 구체 클래스가 아닌 인터페이스를 의존해야 하는 원칙인데, AppConfig 클래스로 구현객체를 생성하고 의존을 연결하는 역할을 해주어 DIP를 만족시킨다.