새로운 할인 정책 개발
요구 사항이 변경되어 새로운 할인 정책을 추가해야 된다.
할인 정책이 기존에는 고정 금액 할인이었으나, 주문 금액 당 할인하는 정률 %할인으로 변경한다. 객체 지향 설계의 원칙을 준수했다면 쉽게 할인 정책을 변경할 수 있을 것이다. 코드를 통해 확인해보자.
RateDiscountPolicy 추가
RateDiscountPolicy.java
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;
}
}
}
테스트코드를 작성해보자.
shift+command+t
package hello.core.discount;
import hello.core.member.Grade;
import hello.core.member.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class RateDiscountPolicyTest {
RateDiscountPolicy discountPolicy = new RateDiscountPolicy();
@Test
@DisplayName("VIP는 10% 할인이 적용되어야 한다")
void vip_o() {
//given
Member member = new Member(1L, "memberVip", Grade.VIP);
//when
int discount = discountPolicy.discount(member, 10000);
//then
Assertions.assertThat(discount).isEqualTo(1000);
}
@Test
@DisplayName("VIP가 아니면 할인이 적용되지 않아야 한다. ")
void vip_x() {
Member member = new Member(1L, "memberVip", Grade.BASIC );
//when
int discount = discountPolicy.discount(member, 10000);
//then
Assertions.assertThat(discount).isEqualTo(1000);
}
}
=> 테스트는 실패하는 경우도 확인해야 한다. 등급이 basic일 때 실패하는지도 확인하자.
새로운 할인 정책 적용과 문제점
실제 적용하려면 클라이언트인 OderServiceImple 을 수정해야 한다.
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
@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);
}
}
=> FixDicountPolicy를 RateDiscountPolicy로 변경한다.
"문제점"
- 역할과 구현을 분리했는가? OK
- 다형성도 활용하고, 인터페이스의 구현 객체를 분리했는가? OK
- OCP, DIP 같은 객체지향 설계원칙을 충실히 준수했는가? => 그렇게 보이지만 사실은 아니다.
- DIP 주문서비스 클라이언트인 OrderServiceImpl 는 DiscountPolicy 인터페이스에 의존하면서 DIP를 지킨 것 같지만 그렇지 않다. 클래스의 의존관계를 분석해보면 추상(인터페이스)뿐안 아니라 구체(구현) 클래스에도 의존하고 있다.
- OCP: 변경하지 않고 확장할 수 있어야 하는데 지금 코드는 기능을 확장하면서 변경하면, 클라이언트 코드에 영향을 준다.
=> 클라이언트인 OrderServiceImpl이 DiscountPolicy 뿐만 아니라 FixDiscountPolicy 인 구체 클래스도 함께 의존하고 있다. DIP를 위반한 것이다.
따라서 FixDiscountPolicy에서 RateDiscountPolicy로 변경하는 순간 OrderServiceImpl도 변경해아 하고 이는 OCP를 위반하게 된다.
어떻게 문제를 해결할 수 있을까?
DIP를 위반하지 않도록 인터페이스에만 의존하도록 의존관계를 변경하면 된다.
OrderServiceImpl.java
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);
}
}
=> 인터페이스에만 의존하도록 코드를 변경했다. 그러나 할인정책이 정해지지 않았기 때문에 nullPointException이 발생한다.
"해결방안"
이 문제를 해결하기 위해서는 누군가가 클라이언트인 OrderServiceImpl에 DiscountPolicy 의 구현 객체를 대신 생성하고 주입해주어야 한다.
다음 포스팅에서 구체적인 해결 방안에 대해 알아 볼 것이다.
* 인프런 '스프링 핵심 원리 -기본편' 강의를 참고하여 작성했습니다.
'웹 개발 > Spring' 카테고리의 다른 글
[Spring 기본] AppConfig 리팩토링 (0) | 2024.08.13 |
---|---|
[Spring 기본] 관심사의 분리 (0) | 2024.08.13 |
[Spring 기본] 주문과 할인 도메인 개발 (0) | 2024.08.08 |
[Spring 기본] 주문과 할인 도메인 설계 (0) | 2024.08.08 |
[Spring 기본] 회원 도메인 개발 (0) | 2024.08.08 |