
미쳤다 미쳤어 김영한 그는 신인가?
애플리케이션의 전체 동작 방식을 구성(Configuration)하기 위해, 구현 객체를 생성하고 연결하는 책임을 가지는 별도의 설정 클래스를 만들자.
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository()); // 생성자 주입
}
public OrderService orderService() {
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
MemberServiceImplMemoryMemberRepositoryOrderServiceImplFixDiscountPolicyMemberServiceImpl → MemoryMemberRepositoryOrderServiceImp → MemoryMemberRepository, FixDiscountPolicypublic 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를 보면 역할과 구현 클래스가 한 눈에 들어온다.