/* 이 글은 김영한님의 강의를 보고 정리하려고 작성한 글입니다. */
새로운 할인 정책 개발
◾ 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라고 하였다. 하지만 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다.
◾ 결국 고정할인 정책에서 정률할인 정책으로 변경을 요구하였다...!
1. 요구사항 및 설계
◾ DiscountPolicy 인터페이스가 있으니 그냥 FixDisCountPolicy -> RateDiscountPolicy만 추가로 개발하여 변경한다.
// 할인 정책 역할
public interface DiscountPolicy {
/**
* 할인 대상 금액
*/
int discount(Member member, int price);
}
2. RateDiscountPolicy 추가 및 테스트
RateDiscountPolicy
// 새로운 할인 정책
@Component // 각 클래스가 컴포넌트 스캔의 대상이 되도록 에노테이션을 붙여줌
@MainDiscountPolicy
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;
}
}
}
Ctrl + Shift + T 를 누르면 테스트 파일을 생성해준다.
RateDiscountPolicyTest
class RateDiscountPolicyTest {
RateDiscountPolicy discountPolicy = new RateDiscountPolicy();
// 할인 대상일 경우 실행될 테스트
@Test
@DisplayName("VIP는 10% 할인이 적용되어야 한다")
void vip_0() {
//given
Member member = new Member(1L, "memberVIP", Grade.VIP);
//when
int discount = discountPolicy.discount(member, 10000);
//then
assertThat(discount).isEqualTo(1000);
}
// 할인 대상이 아닐 경우 실행될 테스트
@Test
@DisplayName("VIP가 아니면 할인이 적용되지 않아야 한다")
void vip_x() {
//given
Member member = new Member(2L, "memberBASIC", Grade.BASIC);
//when
int discount = discountPolicy.discount(member, 10000);
//then
assertThat(discount).isEqualTo(0);
}
}
◾ @DisplayName 은 JUnit5 부터 지원하는 기능으로 테스트 메서드의 이름을 지정해 줄 수 있다.
◾ Assertions는 static import 하는게 좋음 (Alt + Enter 누르고 두번째꺼 선택)
3. 방금 추가한 새로운 할인 정책의 문제점은 무엇일까? --> 새로운 할인 정책 적용해보자
OrderServiceImpl
// orderService 구현체 Impl
@Component // 각 클래스가 컴포넌트 스캔의 대상이 되도록 에노테이션을 붙여줌
@RequiredArgsConstructor // => final이 붙은 필드에 대한 생성자를 자동 만들어 준다. >>> lombok 설치 필수 = @Autowired 사용할 필요xx
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository; //인터페이스에만 의존
private final DiscountPolicy discountPolicy; // 인터페이스에만 의존
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy(); // 구현체에 의존, DIP 위반
// private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
// 생성자(OrderServiceImpl) 에서 여러 의존관계도 한번에 주입 가능
// @MainDiscountPolicy 생성자 자동 주입
// @Autowired
// public OrderServiceImpl(MemberRepository memberRepository, @MainDiscountPolicy 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);
}
//테스트 용도
// public MemberRepository getMemberRepository() {return memberRepository;}
}
◾ 원래 있던 discountPolicy를 FixDiscountPolicy -> RateDiscountPolicy로 변경하면 된다.
◾ But, 이렇게 하면 OCP, DIP를 위반한다.
➔ DIP : OrderServiceImpl은 DiscountPolicy 인터페이스에 의존하며 DIP를 지킨 것 같지만 자세히 보면 구현 클래스에도 의존하고 있음. DIP 위반
➔ OCP : 그래서 RateDiscountPolicy로 변경하려면 OrderServiceImpl의 소스코드도 함께 변경해야 한다. OCP 위반
4. 문제점 해결
OrderServiceImpl
public class OrderServiceImpl implements OrderService{
// private final MemberRepository memberRepository = new MemoryMemberRepository();
private 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);
}
}
◾ 구체에 의존하지 않고 추상화인 인터페이스에 의존하게 코드 변경
◾ But 코드를 실행하면? NullPointerException 발생 (discountPoliy를 구체화하지 않았기때문)
➔ 해결방안 : 이 문제를 해결하려면 누군가 클라이언트인 'OrderServiceImpl'에 'DiscountPolicy'의 구현 객체를 대신 생성하고 주입해주어야 함. ---> Configuration으로 해결
'Spring' 카테고리의 다른 글
Spring JPA :JpaRepository 상속 시 메서드 이름 구성 (0) | 2024.07.25 |
---|---|
AWS : AWS IAM, AWS CLI (0) | 2024.06.21 |
[스프링 핵심 원리 - 기본편] #3 인프런 강의 정리(예제만들기 - 회원 도메인 설계, 개발, 실행, 테스트) (1) | 2024.06.11 |
[스프링 핵심 원리 - 기본편] #2 인프런 강의 정리(예제만들기 - 프로젝트 생성, 비즈니스 요구사항과 설계) (0) | 2024.06.10 |
[스프링 핵심 원리 - 기본편] #1 인프런 강의 정리(객체 지향 설계의 5가지 원칙 SOLID, 객체지향 설계와 스프링) (1) | 2024.06.10 |