쌓고 쌓다
관심사의 분리 with DI? 본문
정리 내용 : 인프런 - 김영한
아래의 의존 관계를 기대하며 설계했다.
OrderServiceImpl (주문 서비스 구현체)
public class OrderServiceImpl implements OrderService {
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}
현재 코드는 할인 정책 Fix(고정), Rate(정률) 변경에 따라
구현체인 Fix, Rate를 갈아끼워야하는 문제가 있다.
즉, OrderServiceImpl가 인터페이스 OrderSerivce에게만 의존하는 것이 아니라
구현체인 Fix, Rate를 함께 의존하고 있다는 것이다.
그래서 정책을 Fix, Rate로 구체 클래스를 변경할때 OrderServiceImpl도 코드 변경을 해야 한다.
이것인 DIP(Dependency Inversion Principle) 의존 역전 원칙에 어긋난다.
OrderServiceImpl를 추상에만 의존하도록 변경해보자.
OrderServiceImpl
public class OrderServiceImpl implements OrderService {
private DiscountPolicy discountPolicy;
}
??? 구현체 없이 인터페이스로 실행은 안된다.
=> 누군가가 OrderServiceImpl에 구현 객체를 생성하고 주입해주면 해결이 가능하다.
관심사의 분리
OrderServiceImpl는 인터페이스 DiscountPolicy의 역할만 알면 충분하지
DiscountPolicy에 어떤 구현 Fix, Rate가 올지 알고 있어야하는, 정하는 구현을 필요하지 않다.
AppConfig
전체 동작 방식을 구성(config)하기 위해 구현 객체를 생성하고 연결하는 책임을 가지는 설정 클래스이다.
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
즉, 인터페이스에 구현 객체를 생성해서 넣어주는 동작을 한다.
이제 아래와 같이 AppConfig를 통해 memberService와 orderService를 사용할 수 있다.
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
OrderService orderService = appConfig.orderService();
OrderServiceImpl
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는 MemberRepository, DiscountPolicy 추상에만 이제 의존한다. 구현은 관심사 밖이다.
=>
클라이언트인 OrderServiceImpl 입장에서
의존 관계를 외부에서 주입해주는 것 같이 보여 DI(Dependency Injection)
의존 관계 주입 또는 의존성 주입이라고 한다.
AppConfig 리팩터링
중복된 부분이 존재하고, MemberRepository, DiscountPolicy 등... 역할과 구현이 한눈에 들어오지 않음.
리팩터링 후
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
public DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
}
이제 역할이 메서드명으로 다 들어나고 전체 애플리케이션 구성을 한눈에 볼 수 있다.
할인 정책의 구현이 Fix에서 Rate로 변경이 되어도
discountPolicy 역할의 구현체를 "new RateDiscountPolicy"로 바꾸기만하면 모두 적용이 된다.
'프로그래밍 > spring' 카테고리의 다른 글
스프링 컨테이너, 스프링 빈 조회 (0) | 2023.07.08 |
---|---|
IoC, DI, 컨테이너 (0) | 2023.07.07 |
[스프링 부트] 게시글 목록 페이징 - 8 (0) | 2023.07.03 |
[스프링 부트] 게시글 수정 - 7 (0) | 2023.07.01 |
[스프링 부트] 게시글 작성 유효성 검사 및 폼 데이터 유지 - 6 (0) | 2023.06.30 |