오봉이와 함께하는 개발 블로그

Spring - 스프링 핵심 원리 이해2 - 1(객체 지향 원리 적용) 본문

BE/Spring

Spring - 스프링 핵심 원리 이해2 - 1(객체 지향 원리 적용)

오봉봉이 2022. 5. 31. 23:16
728x90

새로운 할인 정책

  • 기존 고정 1000원 할인에서 금액에 따라 10% 할인으로 변경

RateDiscountPolicy 추가

RateDiscountPolicy 코드

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;
        }
    }
}

RateDiscountPolicy 테스트

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() {
        // given
        Member member = new Member(2L, "memberBASIC", Grade.BASIC);
        // when
        int discount = discountPolicy.discount(member, 10000);
        // then
        Assertions.assertThat(discount).isEqualTo(0);
    }
}

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

할인 정책 적용

할인 정책을 변경하려면 클라이언트인 OrderServiceImpl을 고쳐야 함.

public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository = new MemoryMemberRepository();
    // private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();

}

문제점

  • 역할과 구현을 충실하게 분리했다 -> OK
  • 다형성 활용, 인터페이스와 구현 객체 분리 -> OK
  • OCP, DIP 같은 객체지향 설계 원칙을 충실히 준수했다. -> NO
    • DIP
      • 주문서비스 클라이언트(OrderServiceImpl)는 인터페이스뿐만 아니라 구현 클래스에도 의존하고 있다
        • 인터페이스 의존 : DiscountPolicy
        • 구현 클래스 : FixDiscountPolicy, RateDiscountPolicy
기대했던 의존

실제 의존

DIP 위반!!!

정책 변경

FixDiscountPolicy 를 RateDiscountPolicy 로 변경하는 순간 OrderServiceImpl 의 소스 코드도 함께 변경 -> OCP 위반

public class OrderServiceImpl implements OrderService{

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

}

어떻게 문제를 해결?

  • 클라이언트 코드(OrderServiceImpl)은 DiscountPolicy의 인터페이스 뿐만 아니라 구체 클래스도 함께 의존
    • 구체 클래스를 변경하려면 클라이언트 코드도 함께 변경해야 한다.(정책 변경 참고)
    • DIP 위반 -> 인터페이스만 의존하도록 변경

인터페이스에만 의존하도록 변경

   public class OrderServiceImpl implements OrderService {
      //private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
      private DiscountPolicy discountPolicy;
}
  • 구현체가 없기 때문에 실행하면 null pointer exception 발생!

해결 방안

  • 누군가가 클라이언트인 OrderServiceImpl에 DiscountPolicy의 구현 객체를 대신 생성하고 주입해주어야 한다.
출처 : 인프런 김영한 지식공유자님의 스프링 완전 정복 로드맵 강의
728x90
Comments