애플리케이션을 하나의 공연이라 생각해보자.
각각의 배역이라하면 인터페이스라고 하면,
로미오나 줄리엣의 역할을 누가 정할지는 배우(구현체)가 정하는게 아니다.
로미오 역할 = 디카프리오? 마동석?
배우가 역할을 하고 싶다고 그 역할을 할 수 있는게 아니다.
관심사를 분리해야한다.
AppConfig 가 필요한 이유
어플리케이션의 전체 동작을 구성하기 위해, 구현 객체를 생성하고 연결하는 책임을 가지는 별도의 설정 클래스가 필요함
어떤 배우가 어떤 역할을 할지 정해주는 책임을 가진 공연기획자가 필요한 것과 마찬가지이다.
AppConfig는 애플리케이션의 실제 동작에 필요한 구현 객체를 생성한다.
AppConfig는 생성한 객체 인스턴스의 참조를 생성자를 통해서 주입해준다.
public MemberService memberService(){
return new MemberServiceImpl(new MemoryMemberRepository());
}
코드로 봐보자
public class AppConfig {
// MemberServieImpl 구현체가 생성될 때, MemberRepository 상속받은 repository 구현체를 주입함
public MemberService memberService(){
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderServie orderService(){
return new OrderServiceImpl(new MemoryMemberRepository(),new FixDiscountPolicy());
}
}
appConfig라는 객체의 책임은 service 구현 객체를 만드는 것이며, service 구현 객체가 만들어질때 필요한 의존관계인 객체들을 주입해준다.
public class MemberServiceImpl implements MemberService{
// MemberRepository 추상에만 의존하면 됨! 구체 클래스에 대해서 몰라도 된다!
private final MemberRepository memberRepository;
// MemberServieImpl 입장에서는 어떤 memberRepository 객체가 들어올지 모름
// 다형성에 의해서 MemberRepository 인테페이스를 상속받은 다양한 객체가 들어올 수 있음
// 어떤 객체가 주입 될지는 오직 AppConfig에서 결정된다.
// MemverServiceImpl은 이제부터 의존관계에 대한 고민은 외부에 맡기고, 실행에만 집중하면 됨!!
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);
}
}
MemberServiceImpl 객체는 MemberService의 역할을 구현하는 구현체이다. 그렇기에 MemberServiceImpl 객체의 책임은 MemberService의 역할을 수행하는 것이다. 그것은 memoryRepository에 회원을 저장하고,memoryRepository에서 회원을 찾는 것이다. 이 때 어떤 memoryRepository일지는 신경쓰지 않는다. 왜냐하면 구체적인 것에 의존하지 않고 추상적인 것에 의존하는 것이 변경에 유연하기 때문이다.