
public class OrderServiceImpl implements OrderService {
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}
문제 : 정책 갈아끼울 시, OrderServiceImpl 코드 수정해야함 -> OCP, DIP 안 지켜짐.
ServiceImpl 이 구현체(RateDiscountPolicy,FixDiscountPolicy ) 까지 의존하고있음.추상에만 의존하도록 수정하면 되겠네.
public class OrderServiceImpl implements OrderService {
//private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
private DiscountPolicy discountPolicy;
}
Q. 구현 객체 빼긴 했는데, 이대로는 실행 안됨. 변수에 객체가 들어가긴 해야함. 어떻게 하지?
-> 책임 분리해보자. 역할을 부여해주는 객체도 따로 만들자 -> AppConfig
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(
new MemoryMemberRepository(),
new FixDiscountPolicy());
}
}
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public void join(Member member) {
memberRepository.save(member);
}
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
MemberServiceImpl 생성자 만들고 매개변수 받음 -> 이 매개변수로 구현체가 들어옴.Appconfig 클래스 안에 함수 만듦.return으로 구현체 반환.Appconfig appconfig = new Appconfig();
MemberService memberService = appconfig.memberService();
appconfig.memberService()의 반환 값은?
-> new MemberServiceImpl(new MemoryMemberRepository());
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();
}
}
나아진 점 :
1. MemberRepository, DiscountPolicy 따로 뺌으로서 중복 제거
-> 원래 meberRepository 변경시 두 번 수정해야했음.
2. 흐름 파악에 유리해짐.
AppConfig 등장 전 : 구현체가 구현체를 생성, 연결, 실행까지 다 함. 구현체가 제어 흐름을 조종. 개발자 입장에서는 자연스러운 흐름.
AppConfig 등장 후 : 구현체는 자신의 로직 실행만을 담당함. 제어 흐름은 AppConfig가 담당.
-> 프로그램 제어 흐름을 직접 제어 X, 외부에서 관리 => 제어의 역전(IoC)
OrderServiceImpl 은 DiscountPolicy 인터페이스에 의존. 실제 어떤 구현체가 사용될 지는 모름.애플리케이션 실행 안해도 알 수 있음.
예를들어, OrderServiceImpl은 MemberRepository, DiscountPolicy에 의존함을 앎. 하지만 정확히 어떤 구현체가 주입 될지는 알 수 없음.
애플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조가 연결된 의존 관계.

애플리케이션 실행 시점에 외부에서 구현체를 생성하고, 클라이언트와 서버의 실제 의존관계 연결 -> 의존관계 주입
AppConfig 처럼 객체 생성, 관리하며 의존관계를 연결해 주는 것
- 최근에는 주로 DI 컨테이너라 함.
@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 붙여줌 -> 스프링 컨테이너에 스프링 빈으로 등록. public static void main(String[] args) {
// AppConfig appConfig = new AppConfig();
// MemberService memberService = appConfig.memberService();
ApplicationContext applicationContext = new
AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
}
ApplicationContext 를 스프링 컨테이너라고 함.AppConfig로 DI 했지만, 이젠 스프링 컨테이너 사용.@Configuration 붙은 것을 설정 정보로 사용함. 그리고 @Bean 적힌 메서드 모두 호출하여 반환된 객체를 스프링 컨테이너에 등록함. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라 함.@Bean 붙은 메서드 이름 (memberService, orderService)applicationContext.getBean("스프링 빈 이름", 클래스명.class)