Ch. 3 (AppConfig, DI, 스프링 컨테이너)
1. 정률 할인 정책으로 변경
public class OrderServiceImpl implements OrderService {
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}
- RateDiscountPolicy() 구현체에 의존 —> DIP 위반
실제 의존관계
- DiscountPolicy 인터페이스와 FixDiscountPolicy 구현체를 동시에 의존하고 있음
해결방안
- OrderServiceImpl에 DiscountPolicy의 구현 객체를 외부에서 대신 주입 —> DIP
public class OrderServiceImpl implements OrderService {
private DiscountPolicy discountPolicy;
}
2. AppConfig
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(
new MemoryMemberRepository(),
new FixDiscountPolicy());
} }
MemberServiceImpl - 생성자 주입
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
- 더이상 MemberServiceImpl은 구현체(MemoryMemberRepository)에 의존하지 않는다.
- MemberRepository(인터페이스)만 의존
- 객체 주입은 외부(AppConfig)가 담당
- MemberServiceImpl은 실행(find, join)만 집중
DIP 완성
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;
}
- MemberServiceImpl과 같은 결과(실행에만 집중, 주입은 AppConfig가 담당)
3. AppConfig 리팩토링
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
}
- 중복 제거, 역할에 따른 구현이 잘 보이게 수정
구성, 사용 영역의 구분
DI
- 정적인 클래스 의존관계 - 동적인 객체 의존관계 구분
- DI를 통해 정적인 클래스 의존관계 변경 없이 동적인 객체 의존관계를 변경
- 클라이언트 코드 변경 없이 클라이언트가 호출하는 대상의 타입 인스턴스 변경이 가능
- 클래스 다이어그램
- 객체 다이어그램
4. Spring으로 전환
AppConfig
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
- @Configuration : 설정을 구성한다.
- @Bean : 해당 메서드를 호출해 반환된 객체를 스프링 컨테이너에 등록, 등록된 객체가 스프링 빈
MemberApp에 스프링 컨테이너 적용
public class MemberApp {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
...
OrderApp에 스프링 컨테이너 적용
public class OrderApp {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
- ApplicationContext == 스프링 컨테이너