스터디/스프링 핵심 원리 - 기본편

스프링 핵심 원리 이해2 - 객체 지향 원리 적용 [새로운 할인 정책]

멩주 2022. 9. 11. 02:38

애자일 소프트웨어 개발 선언

공정과 도구보다 개인과의 상호작용
포괄적인 문서보다 작동하는 소프트웨어를
계약 협상보다 고객과의 협력을
계획을 따르기보다 변화에 대응하기를

 

새로운 할인 정책 개발

 

RateDiscountPolicy

public class RateDiscountPolicy implements DisvountPolicy{

    private int discountPercent = 10;
    @Override
    public int discount(Member member, int price) {
        if (member.getGrade() == Grade.VIP){
            return price * discountPercent / 100;
        }
        return 0;
    }
}

 

RateDiscountPolicyTest

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(){
        //given
        Member member = new Member(1L, "memberBASIC", Grade.BASIC);

        //when
        int discount = discountPolicy.discount(member, 10000);

        //then
        Assertions.assertThat(discount).isEqualTo(0);
    }
}

test의 경우 실패값도 확인해야 한다.

 


새로운 할인 정책 적용과 문제점

실제 새로운 할인 정책을 적용하기 위해서 클라이언트인 OrderServiceImpl을 수정해야 한다.

public class OrderServiceImpl implements OrderService{

//    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}

 

 

문제점

  • 역할과 구현 분리
  • 다형성 활용, 인터페이스와 구현 객체 분리
  • OCP, DIP 같은 객체 지향 설계 원칙 준수?
  • DIP: 주문 클라이언트인 OrderServiceImpl은 DiscountPolicy 인터페이스에 의존하면서 DIP를 지킨거 아니야?
    • 클래스 의존관계를 살펴보면 인터페이스 뿐만 아니라 구현 객체(클래스, Fixed or RateDiscountPolicy)에 의존하고 있다.
    • 인터페이스: DiscountPolicy/ 구현 클래스: Fixe or RateDiscountPolicy
  • OCP: 변경하지 않고 확장할 수 있다는데?
    • 클라이언트인 OrderServiceImpl의 수정이 일어났다!

 

기대와 실제 의존관계가 다르다.

클라이언트인 OrderServiceImpl이 DiscountPolicy 인터페이스 뿐만 아니라 FixDiscountPolicy인 구체 클래스도 함께 의존하고 있다. new를 통해서!

DIP 위반!

 

또한 이로 인해 OrderServiceImpl 즉 클라이언트의 코드가 변경된다.

OCP 위반!

 

이 문제를 어떻게 해결할 수 있을까?

  • 클라이언트 코드인 OrderServiceImpl은 DiscountPolicy의 인터페이스 뿐만 아니라 구현 클래스도 함께 의존한다.
  • 그래서 구체 클래스를 변경 시, 클라이언트 코드도 함께 변경된다.
  • DIP 위반 -> 추상에만 의존하도록 변경
  • DIP를 위반하지 않도록 인터페이스에만 의존하도록 의존관계를 변경하면 된다.

 

public class OrderServiceImpl implements OrderService{
    private DiscountPolicy discountPolicy;
}

final은 값이 무조건 할당되어야 한다.

  • 인터페이스에만 의존하도록 설계 코드 변경
  • 그런데 구현체가 없는데 어떻게 코드를 실행할 수 있을까? <- NullPointerException

 

클라이언트인 OrderServiceImpl에 DiscountPolicy의 구현 객체를 대신 생성하고 주입하자!