객체 지향 원리 적용 - 새로운 할인 정책 개발 및 적용 과 문제점

 

주문과 할인 도메인 개발 및 테스트

 

write-read.tistory.com

애자일 소프트웨어 개발 선언 (agilemanifesto.org)

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

RateDiscountPolicyTest

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.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

class RateDiscountPolicyTest {

    RateDiscountPolicy rateDiscountPolicy = new RateDiscountPolicy();

    @Test
    @DisplayName("VIP는 10% 할인이 적용되어야 한다.")
    void VIP_할인_성공() {
        //given
        Member member = new Member(1L, "memberVIP", Grade.VIP);

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

        //then
        assertThat(discount).isEqualTo(1000);
    }

    @Test
    @DisplayName("VIP가 아니면 할인이 적용되지 않아야 한다.")
    void VIP_할인_실패(){
        //given
        Member member = new Member(1L, "memberBASIC", Grade.BASIC);

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

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

문제점

클라이언트인 OrderServiceImpl 코드를 고쳐야한다.

package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;

public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository = new MemoryMemberRepository();
//    private final DiscountPolicy discountPolicy = new FixDiscountPolicy(); // 변경 전
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();  // 변경 후

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member , itemPrice);

        return new Order(memberId , itemName , itemPrice , discountPrice);
    }
}

 

  • 역할과 구현을 충실한게 분리 했다.
  • 다형성도 활용하고 , 인터페이스와 구현 객체를 분리했다.
  • OCP , DIP 같은 객체지향 설계 원칙을 충실히 준수했다.
    • 그렇게 보이지만 사실은 아니다.
    • DIP : 주문 서비스 클라이언트(OrderServiceImpl)는 DiscountPolicy에 의존하면서 DIP를 지킨것 같지만
      • 클래스 의존관계를 분석해 보면 , 추상(인터페이스)뿐만 아니라 구체(구현) 클래스에도 의존 하고 있다.
      • 추상(인터페이스) 의존 : DiscountPolicy
      • 구체(구현) 클래스 : FixDiscountPolicy , RateDiscountPolicy
    • OCP : 코드를 변경하지 않고 확장해야 하는데 지금 코드는 기능을 확장해서 변경하면 클라이언트 코드에 영향을 준다. 따라서 OCP를 위반한다.

 

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

DIP를 위반하지 않도록 인터페이스에만 의존하도록 의존관계를 변경하면 된다.

 

OrderServiceImpl

package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;

public class OrderServiceImpl implements OrderService{

    private final MemberRepository memberRepository = new MemoryMemberRepository();
    private DiscountPolicy discountPolicy;	// 변경 후

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member , itemPrice);

        return new Order(memberId , itemName , itemPrice , discountPrice);
    }
}

 

  • 인터페이스에만 의존하도록 코드를 변경했다.
  • 하지만 실제 구현체가 없어 NPE가 발생한다.
  • 해결방안
    • 이 문제를 해결하려면 누군가가 클라이언트인 OrderServiceImpl에 DiscountPolicy구현 객체를 대신 생성하고 주입 해주어야 한다.

 

 

객체 지향 원리 적용 - 관심사의 분리

 

write-read.tistory.com

 

 

스프링 핵심 원리 - 기본편 - 인프런

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다. 초급 프레임워크 및 라이브러리 웹 개발 서버 개발 Back-End Spring 객체지향 온

www.inflearn.com