본문 바로가기
Spring

[Spring] 자바 프로젝트 생성 - 인터페이스와 구현의 구분, DIP / OCP 규칙 위반 예시

by jelliclesu 2024. 9. 21.

비즈니스 요구사항과 설계

  • 회원
    • 회원 가입, 회원 조회 기능
    • 일반 / VIP 등급
    • 회원 데이터: 자체 DB 구축 or 외부 시스템과 연동(미확정)
  • 주문과 할인 정책
    • 회원은 상품 주문 가능
    • 회원 등급에 따라 할인 정책을 적용
    • 할인 정책: 변경 가능성 높음 (모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용 or 정률 적용 or 미적용)
  • 회원 데이터할인 정책: 지금 결정하기 어려움. 그렇다고 이런 정책이 결정될 때까지 개발을 무기한 기다릴 수도 없음.
    ➡️ 인터페이스를 만들고 구현체를 언제든지 갈아끼울 수 있도록 설계
  • 참고: 우선 스프링 없는 순수한 자바로만 개발을 진행

회원 도메인 설계

회원 도메인 요구사항

  • 회원 가입, 회원 조회 기능
  • 일반 / VIP 등급
  • 회원 데이터: 자체 DB 구축 or 외부 시스템과 연동(미확정)

회원 도메인 협력 관계

 

회원 클래스 다이어그램(정적)

 

 

회원 객체 다이어그램(동적)

 

 

회원 도메인 개발 & 실행 & 테스트

  • 회원 엔티티
    • 회원 등급: member>Grade.java (Eunm)
    • 회원 엔티티: member>Member.java
  • 회원 저장소
    • 회원 저장소 인터페이스: member>MemberRepository.java
    • 메모리 회원 저장소 구현체: member>MemoryMemberRepository.java
      (데이터베이스가 확정이 안되어, 가장 단순한 메모리 회원 저장소 구현)
  • 회원 서비스
    • 회원 서비스 인터페이스: member>MemberService.java
    • 회원 서비스 구현체: member>MemberServiceImpl.java
  • 회원 도메인: MemberApp.java
  • 회원 가입 테스트: test>MemberServiceTest.java

 

⚠️ 회원 도메인 설계 문제점

  • 의존 관계가 인터페이스뿐만 아니라 구현까지 모두 의존 ➡︎ OCP, DIP 주순 X

주문과 할인 도메인 설계

주문과 할인 정책

  • 회원은 상품 주문 가능
  • 회원 등급에 따라 할인 정책을 적용
  • 할인 정책: 변경 가능성 높음 (모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용 or 정률 적용 or 미적용)

주문 도메인, 협력, 역할, 책임

  1. 주문 생성: 클라이언트 - 주문 서비스에 주문 생성을 요청
  2. 회원 조회: 주문 서비스 - 회원 저장소에서 회원 조회(할인을 위해 회원 등급 확인)
  3. 할인 적용주문 서비스 - 회원 등급에 따른 할인 여부를 할인 정책에 위임
  4. 주문 결과 반환주문 서비스 - 할인 결과를 포함한 주문 결과 반환

 

주문 도메인 전체

  • 역할과 구현을 분리해서 자유롭게 구현 객체를 조립할 수 있게 설계
    ➡️ 회원 저장소, 할인 정책 유연하게 변경 가능

주문 도메인 클래스 다이어그램

 

주문 도메인 객체 다이어그램1

  • 회원을 메모리에서 조회하고, 정액 할인 정책을 지원해도 주문 서비스를 변경하지 않아도 됨. 협력 관계 그대로 재사용 가능

 

주문 도메인 객체 다이어그램2

  • 회원을 실제 DB에서 조회하고, 정률 할인 정책을 지원해도 주문 서비스를 변경하지 않아도 됨. 협력 관계 그대로 재사용 가능

 

주문과 할인 도메인 개발 & 실행 & 테스트

  • 할인
    • 할인 정책 인터페이스: discount>DiscountPolicy.java
    • 정액 할인 정책 구현체: discount>FixDiscountPolicy.java
    • 정률 할인 정책 구현체: discount>RateDiscountPolicy.java
  • 주문 서비스
    • 주문 서비스 인터페이스: order>OrderService.java
    • 주문 서비스 구현체: order>OrderServiceImpl.java
  • 주문 엔티티: order>Order.java
  • 주문 도메인: OrderApp.java
  • 테스트
    • 주문/할인 테스트: test>OrderServiceTest.java
    • 정률 할인 테스트: test>RateDiscountPolicyTest.java

 

할인 정책 변경

  • 할인 정책을 변경하려면 클라이언트인 OrderServiceImpl 코드를 고쳐야 함
 public class OrderServiceImpl implements OrderService {
 //    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
     private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
 }

 

 

⚠️ 문제점

  • DIP 위반: 주문 서비스 클라이언트(OrderServiceImpl)는 추상(인터페이스) 뿐만 아니라 구체(구현) 클래스에도 의존
    • 추상(인터페이스) 의존: DiscountPolicy
    • 구체(구현) 클래스 의존: FixDiscountPolicy, RateDiscountPolicy
  • OCP 위반: 기능 확장해서 변경하려면 클라이언트 코드에 영향을 줌
    • FixDiscountPolicy  RateDiscountPolicy 로 변경하는 순간 OrderServiceImpl 의 소스 코드도 함께 변경

 

‼️ 해결

  • DIP 위반 - 추상(인터페이스)에만 의존하도록 의존 관계 변경
 public class OrderServiceImpl implements OrderService {
     //private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
     private DiscountPolicy discountPolicy;
  • 구현체가 없어서 코드 실행 불가 
    ➡️ 누군가가 클라이언트인 OrderServiceImpl 에 DiscountPolicy 의 구현 객체를 대신 생성하고 주입해주어야 함