본 포스팅은 김영한 님의 스프링 핵심원리 기본편 강의 섹션 3을 듣고 요약한 내용입니다.
1. 기존코드의 문제점
먼저 예제를 하나 만들겠습니다. 무조건 1000원을 할인해주는 FixDiscountPolicy와 구매 금액에 따라 10%를 할인해주는 RateDiscountPolicy가 있다고 하겠습니다. 대충 FixDiscountPolicy는 이런식으로 구현이 될 것입니다.
package hello.core.discount;
import hello.core.member.Grade;
import hello.core.member.Member;
public class FixDiscountPolicy implements DiscountPolicy {
private int discountFixAmount = 1000;
@Override
public int discount(Member member, int price) {
if(member.getGrade()== Grade.VIP){
return discountFixAmount;
}
else return 0;
}
}
RateDiscountPolicy도 이와 비슷하게 구현되었다고 보고, 생략하겠습니다. 자 이제 이 할인정책을 애플리케이션에 적용해보도록 하겠습니다.
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 DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
//주문을 작성
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
int discountPrice = discountPolicy.discount(itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
이제 할인 정책이 바뀔때마다 주석처럼 구현체를 바꾸어주면 됩니다. 뭔가 잘 만든것 같습니다. 우리는 역할과 구현을 충실하게 분리했고, 다형성도 활용하고, 인터페이스와 구현 객체를 분리했습니다.
그런데 앞서 배운 SOLID 법칙에 모두 충족할까요? 아닙니다. 아직 몇가지 문제점이 있습니다.
문제점 발견
OCP, DIP 같은 객체지향 설계 원칙을 충실히 준수하지 못했습니다.
주문서비스 클라이언트(OrderServiceImpl)은 DiscountPolicy 인터페이스에 의존하면서 DIP를 잘 지킨것 같지만,
잘 보면 추상(인터페이스) 뿐만 아니라, 구체 클래스에도 의존하고 있습니다.
//private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
OrderServiceImpl의 이 줄을 봅시다. 여기서 OrderServiceImpl은 인터페이스인 DiscountPolicy 뿐만 아니라, 구체 클래스인 FixDiscountPolicy와 RateDiscountPolicy 모두에 의존하고 있습니다.
즉, 우리가 RateDiscountPolicy에서 FixDiscountPolicy로 변경할 때, 클라이언트 코드(OrderServiceImpl)의 변경이 필요하다는 것입니다. 이것은 곧 OCP 위반이 됩니다.
OCP?
OCP 개방-폐쇄 원칙을 의미하며, 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 합니다. 따라서, 다형성을 활용하여 인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현할 수 있습니다.
DIP?
DIP 의존관계 역전 원칙을 의미하며, 클라이언트는 구현 클래스에 의존하지 말고, 인터페이스에 의존해야 합니다. 클라이언트가 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있습니다.
이해하기 쉽게 사진으로 살펴보겠습니다.
우리가 기대했던 의존관계는 다음과 같습니다.
그런데 현재 의존관계는 다음과 같습니다.
그럼 어떻게 해결할 수 있을까요?
현재 클라이언트 코드인 OrderServiceImpl은 DiscountPolicy의 인터페이스 뿐만 아니라, 구체 클래스도 함께 의존하고 있습니다. 다라서, 인터페이스에만 의존하도록 변경해주면 됩니다.
다음 포스팅에선 인터페이스에만 의존하도록 설계를 변경하는 방법에 대해서 알아보도록 하겠습니다.
인텔리제이 tip) for 문 만들때 iter + tab 하면 for 문 자동완성
soutv -> 변수 출력
cmd + e -> 이전 페이지로 돌아감
isSameAs : 같은 객체인지 확인(인스턴스가 같은지)
isEqualTo : equalTo와 같은 역할을 함