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

Chooooo·2022년 10월 5일
0
post-thumbnail

이 글은 강의 : 김영한님의 - "스프링 핵심원리 - 기본편"을 듣고 정리한 내용입니다. 😁😁


새로운 할인 정책 개발

새로운 할인 정책을 확장해보자
지금까지 할인 정책은 얼마를 주문하던 1000원을 할인해주는 정액 할인 정책이였다. 하지만 맨 처음 기획을 보면 이 정책은 언제든지 바뀔 수 있는 것이였다.

기획자가 이제 1000원이 아닌, 주문 금액의 10퍼센트를 할인해주는 정률 할인 정책으로 변경해달라는 요구가 들어왔다면? 이것을 변경해서 적용해보자.

RateDiscountPolicy 추가

주문한 금액의 10퍼센트를 할인해주는 새로운 정률 할인 정책 추가.

DiscountPolicy를 구현한 정률 할인 정책 클래스를 만든다!

package hello.core.discount;

import hello.core.member.Grade;
import hello.core.member.Member;

public class RateDiscountPolicy implements DiscountPolicy{

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

테스트 작성

discount 메서드가 잘 작동하는지 확인하기 위해서 할인이 적용되어야 하는 경우, 할인이 적용되지 않아야하는 경우 2가지를 테스트하는 메서드를 만들었다.
성공할 경우와 실패할 경우 2가지 모두를 테스트하는 것이 좋은 테스트 코드다.
(전체 케이스를 볼 수 있도록 하는 것이 중요)

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 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
        assertThat(discount).isEqualTo(1000);
    }

    // 실패 테스트
    @Test
    @DisplayName("VIP가 아니면 10% 할인이 적용되지 않아야 한다.")
    void vip_x() {
        // given
        Member member = new Member(2L, "memberBASIC", Grade.BASIC);
        // when
        int discount = discountPolicy.discount(member, 10000);
        // then
        assertThat(discount).isEqualTo(0);
    }
}

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

정률 할인 정책 적용하기

public class OrderServiceImpl implements OrderService {
  //    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
      private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
  }

할인 정책을 FixDicountPolicy를 RateDiscountPolicy 를 위와같이 적용하면 된다. 이 코드는 어떤 상태라고 생각이 될까?
역할과 구현을 분리한 것은 맞다. 다형성도 지키고 인터페이스와 구현 객체를 분리한 것도 맞다. 그렇다면 가장 중요한 객체지향 설계 원칙은 잘 지킨 것이라고 할 수 있을까?

DIP원칙을 생각해보면 '추상화에 의존하고, 구체화에는 의존하면 안된다.'라는 원칙이다.
구현 클래스에 의존하지 말고 인터페이스에 의존하라는 뜻
여기서 OrderServiceImpl은 추상화(DiscountPolicy)를 의존하고 있다. 하지만!! RateDiscountPolicy와 같은 구현 클래스에도 의존을 하고 있기 때문에 DIP를 만족하지 못한다.
그렇다면 OCP는 만족하고 있을까? 변경하지 않고 확장이 되어야 한다고 했는데, 정책이 변경되면서 클라이언트 코드가 변경이 되고 직접 구현객체를 수정해줬다.

클라이언트 코드를 변경해야 하는 이유

기대했던 의존관계는

만들면서 기대한 의존관계는 위와 같다. 구현 객체를 의존하지 않고 추상화된 인터페이스(DiscountPolicy)만 의존하는 것.

하지만 실제 의존관계는 위와 같다. 실제로는 인터페이스(추상화)와 구현 클래스(구체화) 모두를 의존하고 있는 모습이라는 것을 알 수 있다. 그리고 정책을 변경하면 어떻게 될까?


OrderServiceImpl이 구현 클래스에도 의존하고 있기 때문에 소스코드의 변경이 필요하다. 그렇기에 클라이언트 소스코드도 변경해야 하므로 OCP역시 위반!

그렇다면 어떻게 해결해야 할 것인가.

문제 해결

구현클래스에 의존하지 않고 추상화된 인터페이스에만 의존하도록 설계를 변경하자!

위와 같은 그림의 설계가 되도록 코드를 변경하자.

public class OrderServiceImpl implements OrderService {
      //private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
      private DiscountPolicy discountPolicy;
}

이렇게 코드를 변경하면 기존에 원했던 의존관계가 나온다. 그런데 실제로 실행해보면 에러가 뜬다. (구현체가 없기 때문! 😥)

<해결>
이것을 해결하려면 누군가 OrderServiceImpl에 DiscountPolicy의 구현객체를 대신 생성하고 주입해줘야 한다.

profile
back-end, 지속 성장 가능한 개발자를 향하여

0개의 댓글