객체 지향 원리 적용

새로운 할인 정책

악덕 기획자: 서비스 오픈 직전에 할인 정책을 지금처럼 고정 금액 할인이 아니라 좀 더 합리적인 주문 금액 당 할인하는 정률 % 할인으로 변경하고 싶어요. 
예를 들어서 기존 정책은 VIP가 10000원을 주문하든 20000원을 주문하든 항상 1000원을 할인했는데, 이번에 새로 나온 정책은 10%로 지정해두면
고객이 10000원 주문시 1000원을 할인해주고, 20000원 주문시에 2000원을 할인해주는 거에요!

순진 개발자: 제가 처음부터 고정 금액 할인은 아니라고 했잖아요.

악덕 기획자: 애자일 소프트웨어 개발 선언 몰라요? “계획을 따르기보다 변화에 대응하기를”

순진 개발자: ... (하지만 난 유연한 설계가 가능하도록 객체지향 설계 원칙을 준수했지 후후)

객체 지향적으로 설계했다면 RateDiscountPolicy로 바꾸기만 하면 될 것이다. 확인해보자.

public class RateDiscountPolicy implements DiscountPolicy{

  private int discountPercent = 10;

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

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

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

할인 정책을 변경하려면 클라이언트인 OrderServiceImpl 코드를 FixDiscountPolicy에서 RateDiscountPolicy로 고쳐야 한다.

우리는 역할과 구현을 충실하게 분리했다. 다형성도 활용하고 인터페이스와 구현 객체를 분리했다. OCP, DIP 같은 객체 지향 설계 원칙은 충실히 준수한 것 같지만 사실은 아니다.

DIP

주문 서비스 클라이언트 OrderServiceImpl은 DiscountPolicy 인터페이스에 의존하면서 DIP를 지킨 것 같은데?!

클래스 의존 관계를 분석해보면 추상(인터페이스) DiscountPolicy 뿐만 아니라 구체(구현) FixDiscountPolicy, RateDiscountPolicy 클래스에도 의존하고 있다.

지금까지는 단순히 DiscountPolicy 인터페이스에만 의존한다고 생각했다.

하지만 잘 보면 클라이언트인 OrderServiceImplDiscountPolicy뿐만 아니라 구체 클래스인 FixDiscountPolicy도 의존하고 있다. DIP를 위반하는 것이다.

OCP

OrderServiceImpl이 인터페이스만 보는 게 아니라 FixDiscountPolicy도 보고 있기 때문에 RateDiscountPolicy로 변경하는 순간 OrderServiceImpl의 소스 코드도 함께 변경해야 한다.

기능을 확장해서 변경하면 클라이언트 코드에 영향을 주므로 OCP를 위반한다.

기름차를 전기차로 변경했는데 라이센스를 다시 따야하는 상황이 온 것이다.

어떻게 해결할 수 있을까?

인터페이스에만 의존하도록 설계를 변경하자.

public class OrderServiceImpl implements OrderService{

  private DiscountPolicy discountPolicy;

  ...
}

인터페이스에만 의존하도록 설계와 코드를 변경했다. 그런데 구현체가 없는 상태에서 코드를 실행하면 NPE가 발생한다.

이 문제를 해결하려면 누군가가 클라이언트인 OrderServiceImplDiscountPolicy의 구현 객체를 대신 생성하고 주입해줘야 한다.

Last updated