"계획을 따르기 보다 변화에 대응하기를"
참고 : 애자일 소프트웨어 개발 선언 https://agilemanifesto.org/iso/ko/manifesto.html
이전 개발의 도메인 설계를 살펴보자.
이번에는 할인 정책을 변경해보려고 한다. 객체 지향의 원리를 적용하여 새로운 할인 정책을 추가한다.
회원 등급에 따라 주문한 금액의 %를 할인해주는 새로운 정률 할인 정책을 추가하자.
package hello2.core2.discount;
import hello2.core2.member.Grade;
import hello2.core2.member.Member;
public class RateDiscountPolicy implements DiscountPolicy {
int discountPercent = 10;
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.Vip){
return price * discountPercent/100;
}
else {
return 0;
}
}
}
작성한 코드가 정상 작동하는 지 확인하기 위하여 Test를 진행한다.
단축키 : Ctrl + Shilft + t -> create new Test
package hello2.core2.discount;
import hello2.core2.member.Grade;
import hello2.core2.member.Member;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;
class RateDiscountPolicyTest {
DiscountPolicy discountPolicy = new RateDiscountPolicy();
@Test
@DisplayName("10% discount should be adapted to VIP")
void vip_o() {
//given
Member member = new Member(1L, "memberVIP", Grade.Vip);
//when
int discount = discountPolicy.discount(member, 10000);
//then
assertThat(discount).isEqualTo(1000);
}
}
할인정책을 추가하고 테스트까지 완료하였다.
새로운 할인 정책을 애플리케이션에 적용해보자.
할인 정책을 변경하기 위해 클라이언트인 OrderServiceImpl 코드를 고친다.
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
//private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
기존의 정액 할인 정책을 주석처리하고, 정률 할인 정책을 추가하였다.
이전에 작성한 OrderServiceTest를 실행하면 정상 작동한다.
그렇다면 우리는 스프링의 SOLID 원칙을 준수하여 잘 작성한걸까?
답은 아니다.
우리는 역할과 구현을 충실하게 분리했다.
다형성도 활용하고, 인터페이스와 구현 객체를 분리했다.
OCP, DIP 같은 객체지향 설계 원칙을 충실히 준수했다.
--> 그렇게 보이지만 그렇지 않다.
DIP : 주문서비스 클라이언트(OrderServiceImpl)는 DiscountPolicy 인터페이스에 의존하면서 DIP를 지킨 것 같은데?
--> 클래스 의존관계를 분석하면, 추상(인터페이스)뿐만 아니라 구체(구현) 클래스에도 의존하고 있다.
- 추상(인터페이스) 의존 : DiscountPolicy
- 구체(구현) 클래스 : FixDixcountPolicy, RateDiscountPolicy
OCP : 변경하지 않고 확장할 수 있다고 하였는데, 새로운 정책을 적용하면서 변경하였던 것을 살필 수 있다.
--> 지금 코드는 기능을 확장해서 변경하면, 클라이언트 코드에 영향을 준다! 따라서 OCP를 위반한다.
그렇다면 위 문제들을 어떻게 해결할 수 있을까?
public class OrderServiceImpl implements OrderService {
private fin al MemberRepository memberRepository = new MemoryMemberRepository();
private DiscountPolicy discountPolicy;
//private final DiscountPolicy discountPolicy = new FixDiscountPolicy();