※ 본 게시글은 인프런 스프링 핵심 원리 - 기본편 강의를 바탕으로 작성하였습니다.
강의 내용을 참고하여 개인적으로 정리한 글입니다.
기존 할인 정책은 VIP에게 무조건 1,000원 할인(FixDiscountPolicy)
기획자가 10,000원 주문 시 1,000원 할인, 20,000원 주문 시 2,000원 할인을 원한다..
정률 할인 (VIP는 10%)로 변경을 원한다
→ 우리는 새로운 구현체 RateDiscountPolicy
만들어보자
RateDiscountPolicy 클래스
package hello.core.discount;
import hello.core.member.Grade;
import hello.core.member.Member;
public class RateDiscountPolicy implements DiscountPolicy {
private int discountPercent = 10;
@Override
public int discount(Member member, int price) {
if(member.getGrade() == Grade.VIP) {
return price * discountPercent / 100;
} else {
return 0;
}
}
}
public class OrderServiceImpl implements OrderService{
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
OrderServiceImpl
가 인터페이스 DiscountPolicy
만 의존 하는 것 같나?FixDiscountPolicy
와 RateDiscountPolicy
를 직접 new를 사용하고 있다
FixDiscountPolicy
를 RateDiscountPolicy
로 변경하는 순간 OrderServiceImpl
의 코드도 함께 변경해야하는 경우가 발생한다
따라서 위 코드는 객체 지향 설계 원칙인 DIP 위반, OCP 위반
위 문제를 어떻게 해결을 하면 좋을까??
객체 생성과 연결을 담당하는 별도 클래스 AppConfig
생성
비유하자면 AppConfig
는 공연 기획자, OrderService
는 배우
package hello.core;
import hello.core.Order.OrderService;
import hello.core.Order.OrderServiceImpl;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MembrServiceImpl;
import hello.core.member.MemoryMemberRepository;
public class AppConfig {
public MemberService memberService(){
return new MembrServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService(){
return new OrderServiceImpl(
new MemoryMemberRepository(),
new FixDiscountPolicy()); //쉽게 교체 가능
}
}
AppConfig
는 실제 동작에 필요한 구현 객체 생성
MembrServiceImpl
, MemoryMemberRepository
, OrderSerivceImpl
, FixDiscountPolicy
AppConfig
는 생성한 객체 인스턴스의 참조를 생성자를 통해서 주입(연결)
번외로 테스트 코드를 작성할 때 @BeforeEach
를 사용하면 각 테스트를 실행하기 전에 호출
따라서
MembrServiceImpl
,OrderSerivceImpl
는 실행에만 집중하면 된다
현재 AppConfig
클래스는 중복 있고, 역할에 따른 구현이 명확하게 안보인다
package hello.core;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//실제 동작에 필요한 "구현 객체를 생성"
//생성한 객체 인스턴스의 참조를 "생성자를 통해서 주입(연결)"
//전체 구성이 어떻게 되어있는지 빠르게 파악 가능
@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 FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
//@Bean을 사용하면 해당 코드들이 "스프링 컨테이너"에 등록
💡드디어 스프링 등장!
AppConfig
에 @Configuration
+ @Bean
을 추가하면 스프링 컨테이너에서 getBean
으로 주입 받는다
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
원칙 | 설명 |
---|---|
SRP | OrderSerivceImpl 은 할인 정책까지 신경쓰지 않아도 된다 |
DIP | DiscountPolicy 인터페이스에만 의존한다 |
OCP | 할인 정책 변경 시 AppConfig 만 수정하면 된다 |
IoC - 제어의 역전과 DI - 의존성 주입은 해당 링크에 들어가면 설명이 있습니다
[인프런] 스프링 핵심원리 기본편-객체지향 설계와 스프링
AppConfig를 통해서 객체 지향 설계 원칙을 적용하고, 스프링이 없이도 DI를 구현할 수 있음을 경험하였다 다음 포스팅은 이제 스프링 컨테이너와 스프링 빈을 활용하는 것으로 오겠습니다~
개발 잫하고 싶다,.,