[Spring] 객체 지향 원리 적용 - DIP 위반

jy9922·2022년 8월 16일
0

Spring

목록 보기
14/34
post-thumbnail

객체 지향 원리 적용 🔎

새로운 할인 정책 개발

  • 고정 금액 할인이 아니라 정률 할인으로 변경하고자 한다.
  • 새로 나온 정책은 10%로 지정해두어서 할인을 해주는 것이다.

RateDiscountPolicy

  • 정률 할인 정책 구현 객체
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();
  • 할인 정책을 변경하려면 OrderServiceImpl 코드를 고쳐야 한다.

문제점 🙄

  • 우리는 역할과 구현을 충실하게 분리하였는가?
    → OK
  • 다형성도 활용하고, 인터페이스 구현과 객체를 분리하였는가?
    → OK
  • OCP, DIP 같은 객체지향 설계 원칙을 충실히 준수하였는가?
    → 그렇게 보이지만 사실은 아니다...😱

DIP 위반

주문 서비스 클라이언트 OrderServiceImplDiscountPolicy 인터페이스에 의존하면서 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();

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

  • 클라이언트 코드인 OrderServiceImplDiscountPolicy의 인터페이스 뿐만 아니라 구체 클래스도 함께 의존한다.
  • 그래서 구체 클래스를 변경할 때 클라이언트 코드도 함께 변경해야 한다.
  • DIP 위반 해결 방법 → 추상적인 의존하도록 변경 (인터페이스에만 의존)
  • DIP를 위반하지 않도록 인터페이스에만 의존하도록 의존관계를 변경하면 된다.

  • 인터페이스에만 의존하도록 코드 변경
public class OrderServiceImp implements OrderService{
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    // private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
    private DiscountPolicy discountPolicy;
...
  • 구현체가 없는데 어떻게 코드를 실행할 수 있을까?
    • 실제 실행해보면 NPE(null pointer exception)가 발생한다.
  • 이를 해결하려면 누군가가 클라이언트인 OrderServiceImpl에 DiscountPolicy의 구현 객체를 대신 생성하고 주입해주어야 한다.

0개의 댓글