미쳤다 미쳤어 김영한 그는 신인가?
애플리케이션의 전체 동작 방식을 구성(Configuration)하기 위해, 구현 객체를 생성하고 연결하는 책임을 가지는 별도의 설정 클래스를 만들자.
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository()); // 생성자 주입
}
public OrderService orderService() {
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
MemberServiceImpl
MemoryMemberRepository
OrderServiceImpl
FixDiscountPolicy
MemberServiceImpl
→ MemoryMemberRepository
OrderServiceImp
→ MemoryMemberRepository
, FixDiscountPolicy
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
MemberRepository
인터페이스만 의존한다.MemberServiceImpl
의 입장에서 생성자를 통해 어떤 구현 객체가 주입될지 알 수 없다.MemberServiceImp
의 생성자를 통해서 어떤 구현 객체를 주입할지는 오직 외부(AppConfig
)에서 결정된다.MemberServiceImpl
은 이제부터 의존관계에 대한 고민은 외부에 맡기고, 실행에만 집중하면 된다.AppConfig
가 담당한다.MemberServiceImpl
은 MemberRepository
인 추상에만 의존하면 된다. 이제 구체 클래스를 몰라도 된다.appConfig
객체는 memoryMemberRepository
객체를 생성하고, 그 참조값을 memberServiceImpl
을 생성하면서 생성자로 전달한다.memberServiceImpl
입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 하여 DI(Dependency Injection), 우리말로 의존관계 주입 또는 의존성 주입이라 한다.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;
}
...
}
OrderServiceImpl
은 FixDiscountPolicy
를 의존하지 않는다!DiscountPolicy
인터페이스만을 의존한다.OrderServiceImpl
입장에서 생성자를 통해 어떤 구현 객체가 주입될 지 알 수 없다.OrderServiceImpl
의 생성자를 통해서 어떤 구현 객체를 주입할지는 오직 외부(AppConfig
)에서 결정한다.OrderServiceImpl
은 이제부터 실행에만 집중하면 된다.public class MemberApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
Member member = new Member(1L, "memberA", Grade.VIP);
memberService.join(member);
Member findMember = memberService.findMember(1L);
System.out.println("new Member = " + member);
System.out.println("findMember = " + findMember);
}
}
public class OrderApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
OrderService orderService = appConfig.orderService();
Long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
System.out.println("order = " + order);
}
}
현재 AppConfig를 보면 중복이 있고, 역할에 따른 구현이 잘 안보인다.
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
private DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
private MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
new MemoryMemberRepository()
이 부분의 중복이 제거되었다.MemoryMemberRepository
를 다른 구현체로 변경할 때 한 부분만 변경하면 된다.AppConfig
를 보면 역할과 구현 클래스가 한 눈에 들어온다.