애플리케이션을 하나의 공연이라고 생각해보면,
인터페이스를 배역(배우 역할)
실제 배역을 정할 때 여 주인공, 남 주인공은 배우들이 정하는게 아니다.
하지만 이전에 했던 코드들은
마치 로미오 역할(인터페이스)을 하는 남 주인공(구현체, 배우)이 ------> 줄리엣 역할(인터페이스)을 하는 여자 주인공(구현체, 배우) 직접 초빙하는 것과 똑같다.
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
private DiscountPolicy discountPolicy = new FixDiscountPolicy();
OrderServiceImpl 은 OrderService 관련 로직만 해야하는데,
FixDiscountPolicy 정책을 자기가 직접 선택하며 discountPolicy 에 할당한다..
다시 공연을 예로...
남자 주인공은 공연도 해야하고 여주도 직접 초빙해야 하기 때문에 다양한 책임을 가지게 된다.
관심사를 분리해야 한다!
- 배우는 본인의 역할인 배역을 수행하는 것에만 집중해야 한다.
- 남주는 어떤 여주가 와도 똑같이 공연을 할 수 있어야 한다!
- 공연을 구성하고, 배우를 섭외하고, 책임을 담당하는 별도의 "공연 기획자"가 나올 시점이다.
애플리케이션도 이렇게 개발을 해야 한다!
AppConfig 등장!
- 애플리케이션의 전체 동작 방식을 구성(config)하기 위해, 구현 객체를 생성하고, 연결하는 책임을 가지는 별도의 설정 클래스를 만들자.

기존에 만들었던 MemberServiceImpl 코드에서 new MemoryMemberRepository(); 지우고
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
생성자를 만들어준다. 생성자를 통해서
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
}
AppConfig 통해서 이렇게 해서 확인해보면
MemberServiceImpl 에는 MemoryMemberRepository() 코드가 없고
오로지 MemberRepository 인 인터페이스만 있다!! ---> 추상화에만 의존하는 것이다! --> DIP 를 지켰다.

이러한 부분에서 용어를 생성자를 통해서 객체가 생성된 것으로 해서 생성자 주입이라고 한다.

똑같이 OrderService도 만들어주고

이렇게 되면 OrderServiceImpl 은 Rate가 들어올 지, Fix가 들어올 지는 전혀 모르는 것이다! (마치 대본 보고 공연하듯이 로직만 실행한다.)

파라미터로 들어가는데 주입해준다고 표현한다. (Injection)
다시 반복하면
- 설계 변경으로 MemberServiceImpl 은 MemoryMemberRepository 를 의존하지 않는다!
- 단지 MemberRepository 인터페이스에만 의존한다.
- MemberServiceImpl 입장에서 생성자를 통해 어떤 구현 객체가 들어올지(주입 될 지)는 알 수 없다.
- MemberServiceImpl 의 생성자를 통해서 어떤 구현 객체를 주입할지는 오직 외부( AppConfig )에서 결정된다.
- MemberServiceImpl 은 이제부터 의존관계에 대한 고민은 외부에 맡기고 실행에만 집중하면 된다.

객체의 생성과 연결은 AppConfig가 담당한다.
----> DIP 완성
MemberServiceImpl 은 MemberRepository 인 추상에만 의존하면 된다. 이제 구체 클래스를 몰라도 된다.
----> 관심사의 분리 : 객체를 생성하고 연결하는 역할 / 실행하는 역할이 명확히 분리되었다.

- appConfig 객체는 memoryMemberRepository 객체를 생성하고 그 참조값을 memberServiceImpl 을 생성하면서 생성자로 전달한다.
- 클라이언트인 memberServiceImpl 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 해서
DI(Dependency Injection) 우리말로 의존관계 주입 또는 의존성 주입이라 한다.

OrderServiceImpl 에서도 FixDiscountPolicy 를 의존하지 않는다.
단지 DiscountPolicy 인터페이스만 의존한다!
- OrderServiceImpl 입장에서 생성자를 통해 어떤 구현 객체가 들어올지(주입될지)는 알 수 없다.
- OrderServiceImpl 의 생성자를 통해서 어떤 구현 객체을 주입할지는 오직 외부( `AppConfig` )에서 결정한다.
- OrderServiceImpl 은 이제부터 실행에만 집중하면 된다.
- OrderServiceImpl 에는 MemoryMemberRepository , FixDiscountPolicy 객체의 의존관계가 주입된다.
MemberApp에서
public class MemberApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
// MemberService memberService = new MemberServiceImpl();
Member member = new Member(1L, "memberA", Grade.VIP);
memberService.join(member);
Member findMember = memberService.findMember(1L);
System.out.println("new member = " + member.getName());
System.out.println("find member = " + findMember.getName());
}
}
이렇게 변경을 해준다.
public class OrderApp {
public static void main(String[] args) {
// AppConfig 통해서
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);
System.out.println("order.calculatePrice = " + order.calculatePrice());
}
}
OrderApp도 변경!
이렇게 변경 후 테스트 코드도 수정해주어야 한다.
public class MemberServiceTest {
// 변경 전
// MemberService memberService = new MemberServiceImpl();
MemberService memberService;
@BeforeEach
public void beforEach() {
AppConfig appConfig = new AppConfig();
memberService = appConfig.memberService();
}
이렇게 해주어야 실행하기 전에 할당하고 테스트가 돌아가게끔! (테스트 두번 있음 두 번 돌아간다.)
public class OrderServiceTest {
MemberService memberService;
OrderService orderService;
@BeforeEach
public void beforEach() {
AppConfig appConfig = new AppConfig();
memberService = appConfig.memberService();
orderService = appConfig.orderService();
}
오더서비스도 수정!

테스트 다 성공!
- AppConfig는 공연 기획자다.
- AppConfig는 구체 클레스를 선택한다. 배역에 맞는 담당 배우를 선택! 어떻게 동작해야 할지 전체 구성을 책임진다.
- 이제 각 배우들은 담당 기능을 실행하는 책임만 진다.
다음은 AppConfig 리팩터링!!
'개인 > [java] 스프링 핵심 원리 학습' 카테고리의 다른 글
| 새로운 구조와 할인 정책 적용 (AppConfig만 수정) (0) | 2023.09.09 |
|---|---|
| AppConfig 리팩터링 (0) | 2023.09.09 |
| 새로운 할인 정책 추가 (객체 지향 원리 적용, 문제점···) (0) | 2023.08.28 |
| 주문과 할인 도메인 설계 (0) | 2023.08.21 |
| 프로젝트 생성, 회원 도메인 설계 (0) | 2023.08.20 |
댓글