- 고정 금액 할인이 아니라 정률 할인으로 변경하고자 한다.
- 새로 나온 정책은 10%로 지정해두어서 할인을 해주는 것이다.
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;
// ctrl + shift + t 누르고 test 코드 바로 만들기
}else{
return 0;
}
}
}
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
class RateDiscountPolicyTest {
RateDiscountPolicy rateDiscountPolicy = new RateDiscountPolicy();
@Test
@DisplayName("VIP는 10% 할인이 적용되어야 한다")
// 성공 테스트
void vip_o(){
//given
Member member = new Member(1L, "memberVIP", Grade.VIP);
//when
int discount = rateDiscountPolicy.discount(member, 10000);
//then
// Assertions.assertThat(discount).isEqualTo(1000);
// alt + enter를 통해 Assertions를 static으로 import
assertThat(discount).isEqualTo(1000);
}
@Test
@DisplayName("VIP가 아니면 할인이 적용되지 않아야 한다")
void vip_x(){
Member member = new Member(2L, "memberBASIC", Grade.BASIC);
//when
int discount = rateDiscountPolicy.discount(member, 10000);
//then
assertThat(discount).isEqualTo(0);
}
}
public class OrderServiceImp implements OrderService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
- 우리는 역할과 구현을 충실하게 분리하였는가?
→ OK- 다형성도 활용하고, 인터페이스 구현과 객체를 분리하였는가?
→ OK- OCP, DIP 같은 객체지향 설계 원칙을 충실히 준수하였는가?
→ 그렇게 보이지만 사실은 아니다...😱
주문 서비스 클라이언트
OrderServiceImpl
는DiscountPolicy
인터페이스에 의존하면서 DIP를 지킨거 같은데? 💁🏻♀️
- 클래스 의존 관계를 분석해보자
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
- 추상(인터페이스) 뿐만 아니라 구체(구현) 클래스에도 의존하고 있다.
- 추상(인터페이스) 의존 :
DiscountPolicy
- 구현(구체) 클래스 :
FixDiscountPolicy
,RateDiscountPolicy
- 우리가 생각한 의존 관계
- 실제 의존 관계
클라이언트인 OrderServiceImpl이 DiscountPolicy 인터페이스와 더불어 FixDiscountPolicy인 구체 클래스도 함께 의존하고 있다.
- 만약 정책을 변경하게 되면
FixDiscountPolicy를 RateDiscountPolicy로 변경하는 순간 OrderServiceImpl의 소스 코드도 함께 변경을 해야한다.
public class OrderServiceImp implements OrderService{ private final MemberRepository memberRepository = new MemoryMemberRepository(); // private final DiscountPolicy discountPolicy = new FixDiscountPolicy(); private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
- 클라이언트 코드인
OrderServiceImpl
은DiscountPolicy
의 인터페이스 뿐만 아니라 구체 클래스도 함께 의존한다.- 그래서 구체 클래스를 변경할 때 클라이언트 코드도 함께 변경해야 한다.
- DIP 위반 해결 방법 → 추상적인 의존하도록 변경 (인터페이스에만 의존)
- DIP를 위반하지 않도록 인터페이스에만 의존하도록 의존관계를 변경하면 된다.
public class OrderServiceImp implements OrderService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
// private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
private DiscountPolicy discountPolicy;
...