김영한님의 스프링 핵심 원리 - 기본편 강의
19강 관심사의 분리를 듣고 작성했습니다.
DIP 지키기 - 의존 역전 원칙 (Dependency Inversion Principle)
이전 코드의 문제점
public class OrderServiceImpl implements OrderService {
//private final DiscountPolicy discountPolicy = new FixDiscountPolicy(); //추상에도 의존, 구체에도 의존 OCP 위반
private final DiscountPolicy discountPolicy = new RateDiscountPolicy(); //추상에도 의존, 구체에도 의존 OCP 위반
}
- 인터페이스(주문 역할) 이 구현체(할인)가 주문이 직접 어떤 할인을 선택할지 결정한다.
- 더 쉽게 예시를들면, 배우가 여자 주인공을 누구로 할지 선택해야함 (배우 - 인터페이스, 여자주인공 - 구현체)
가야할 방향
- 배우는 본인의 역할인 배역을 수행에만 집중
- 배우는 어떤 여자주인공이든 똑같이 공연을 해야함
- 새로운 인물이 나타나야 한다.
- 즉, 공연 기획자가 배우와 공연 기획자의 책임을 확실히 분리해야 한다.
- 공연 기획자: 구성, 배우 섭외, 배우 지정
AppConfig 등장
AppConfig는 애플리케이션의 실제 동작에 필요한 구현 객체를 생성한다.
- MemberServiceImpl
- OrderServiceImpl
AppConfig는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해서 주입(연결)해준다.
- MemberServiceImpl -> MemberMemoryRepository
- OrderServiceImpl -> MemoryMemberRepository, FixDiscountPolicy
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
참고로 AppConfig에서 생성자 주입시 반복되는 호출이 있는데, 아래와 같이 리팩토링 할 수 있다. 이는 20강 AppConfig 리팩터링
내용이다.
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
private MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
private DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
}
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
- MemberServiceImpl은 MemberMemoryRepository에 의존하지 않는다.
- 단지, MemberRepository 인터페이스만 의존한다.
- MemberServiceImpl 입장에서 생성자를 통해 어떤 구현 객체가 들어올지 알 수 없다.
- MemberServiceImpl의 생성자를 통해서 어떤 구현 객체를 주입할지는 오직 외부(AppConfig)에서 결정된다.
- MemberServiceImpl은 이제부터 의존관계에 대한 고민은 외부에 맡기고, 실행에만 집중하면 된다. (join, findMember)
public class OrderServiceImpl implements OrderService {
// 완벽하게 DIP을 지키고 있다. interface만 알고 있음. FixDiscountPolicy인지 RateDiscountPolicy인지 알 필요가 없다.
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은 이제부터 의존관계에 대한 고민은 외부에 맡기고, 실행에만 집중하면 된다. (createOrder)
DIP의 완성
객체의 생성과 연결은 AppConfig가 담당한다.
MemberServiceImpl은 MemberRepository 인터페이스(추상)에만 의존하면 된다.
구체 클래스를 몰라도 된다.
관심사의 분리: 객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리되었다.
appConfig객체는 MemberMemoryRepository 객체를 생성하고 그 참조값을 MemberServiceImpl을 생성하면서 생성자로 전달한다. 클라이언트인 MemberServiceImpl의 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것과 같다고 해서 DI(Dependency Injection) 우리말로 의존관계 주입, 의존성 주입 이라고 한다.
'Study > 인프런' 카테고리의 다른 글
스프링 컨테이너(Spring Container)의 배경 (0) | 2023.01.24 |
---|---|
IoC, DI, 그리고 컨테이너 (0) | 2023.01.24 |
OCP와 DIP를 지키기 위한 발걸음 (0) | 2023.01.01 |
좋은 객체 지향 설계의 5가지 원칙 (SOLID) (0) | 2022.12.23 |
[강의 수강] 스프링 핵심 원리 - 기본편 (1) (0) | 2022.12.11 |