🍀 [ 김영한 스프링 핵심 원리 - 기본편 ] Section04. 스프링 핵심 원리 이해2 - 객체 지향 원리 적용
[ 문제점 ]
클라이언트 코드(Service 구현체)에서 할인 정책의 구현체를 직접 생성 및 사용
DiscountPolicy discountPolicy = new FixDiscountPolicy();
=> OCP 위반 : 구현체의 변화가 필요할 때 클라이언트 코드를 수정해야 함
=> DIP 위반 : 인터페이스(역할)이 아닌 구현체를 참조
[ 해결 방안 ]
구성 영역과 사용 영역을 분리하자
구성 영역 : 클라이언트 코드에서 사용하기로 결정한 구현체들 정의
사용 영역 : 주입받은 구현체로 로직 수행 / 사용 영역에서는 각 정책에 대한 구현체는 드러나 있지 않음
구성 영역 - AppConfig
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 RateDiscountPolicy();
//return new FixDiscountPolicy();
}
}
- 생성자 주입 : 현재 시점에서 사용하기로 결정한 구현체 객체를 생성자를 통해 서비스 구현체에 주입
- 역할과 구현 간의 구성이 잘 드러나도록 코드 작성
사용 영역 - MemberServiceImpl, OrderServiceImpl
1 ) MemberServiceImpl
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
2 ) OverServiceImpl
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;
}
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
- 구현체가 아닌 역할만 의존 = ServiceImpl class가 아닌 Service interface를 참조 = 구현체 드러나지 않음
[ IoC와 DI ]
IoC ( 제어의 역전 )
: 프로그램의 제어 흐름을 외부에서 관리하도록 하는 것
클라이언트 코드(Service class)에서 사용할 구현체를 직접 생성하고 활용하는 것이 아니라
AppConfig에서 각 Service class에서 사용할 구현체를 정의하고
Service class는 자신의 로직을 실행하는 역할만 담당하도록 할 때 제어의 역전이 일어났다고 말한다
DI ( 의존관계 주입 )
정적 의존관계 : import문을 통해 명시되어 있는 의존관계
동적 의존관계 : 애플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조가 연결된 의존관계
애플리케이션 실행 시점에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실질적인 의존 관계가 연결되는 것을 의존관계 주입이라 한다
- 클라이언트 코드 변경 없이 클라이언트가 호출하는 대상의 타입 인스턴스(구현체) 변경 가능
- 정적인 클래스 의존관계 변경 없이 동적인 객체 인스턴스 의존관계 변경 가능
[ Spring으로 구현 ]
AppConfig - 구성 영역
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
//return new FixDiscountPolicy();
}
}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
- @Configuration, @Bean 통해서 Spring Container에 객체 저장하고 관리하도록 설정
왜 스프링 컨테이너에서 객체 관리하도록 하는 게 좋은가??
추후 강의에서 설명 예정...