김영한: 요구사항을 보면 회원 데이터, 할인 정책 같은 부분은 지금 결정하기 어려운 부분이다. 그렇다고 이런 정책이 결정될 때
까지 개발을 무기한 기다릴 수 도 없다. 우리는 앞에서 배운 객체 지향 설계 방법이 있지 않은가! 인터페이스를 만들고 구현체를 언제든지 갈아끼울 수 있도록 설계하면 된다.
회원 도메인 요구사항
회원을 가입하고 조회할 수 있다.
회원은 일반과 VIP 두 가지 등급이 있다.
회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)
회원 도메인 협력 관계
아직 DB 저장소와 외부 시스템 연동 저장소는 정해지지 않았다.
회원 클래스 다이어그램
실제 구현은 이런 식으로 인터페이스를 구현한 뒤 임플리먼츠를 할 예정이다.
회원 객체 다이어그램
회원 서비스: MemberServiceImpl
실제 객체는 이런식으로 구현체를 참조한다.
역할과 구현을 분리해서 자유롭게 구현 객체를 조립할 수 있게 설계했다. 덕분에 회원 저장소는 물론이고, 할인 정책도 유연하게 변경할 수 있다.
회원을 메모리에서 조회하고, 정액 할인 정책(고정 금액)을 지원해도 주문 서비스를 변경하지 않아도 된다. 역할들의 협력 관계를 그대로 재사용 할 수 있다.
회원을 메모리가 아닌 실제 DB에서 조회하고, 정률 할인 정책(주문 금액에 따라 % 할인)을 지원해도 주문 서비스를 변경하지 않아도 된다.
협력 관계를 그대로 재사용 할 수 있다.
실제 완성한 구조이다.
OrderService라는 역할의 구현체만 보겠다.
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
@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);
}
}
보게 되면 위 구조처럼 OrderService의 구현체는 MemoryMemberRepo와 FixDiscountPolicy를 의존하고 있다.
할인에는 전혀 관여하지 않고 할인은 오직 discountPolicy에게 맡기면서 SOLID의 SRP(단일 책임 원칙)을 적용하는데 성공했다.
하지만 MemoryMemberRepository와 FixDiscountPolicy를 Service에서 직접 의존하게 되면서 문제가 생긴다.
저 둘에 구현체가 바뀌게 되면 Service도 손 봐줘야하기 때문이다.
이렇게 되면 SOLID에서 OCP(개방-폐쇄-원칙)과 DIP(의존관계 역전 원칙)에서 문제가 생긴다.
이는 앞서 입문편에서 배웠던 DI 컨테이너 기술로 해결할 수 있다.
오직 하나의 컴포넌트를 싱글톤으로 인젝션 해주면서 이를 해결할 수 있다.
이에 대해서는 이후에 더 알아보자