본문 바로가기
Study/Java Spring Boot

[Spring] 핵심 원리 2-2 주문, 할인 도메인 개발과 테스트 (DIP위반)

본 글은 김영한님의 강의 내용을 바탕으로 정리한 글입니다.

 

스프링 핵심 원리 - 기본편 - 인프런 | 학습 페이지

지식을 나누면 반드시 나에게 돌아옵니다. 인프런을 통해 나의 지식에 가치를 부여하세요....

www.inflearn.com

 

1. 주문과 할인 도메인 설계

주문과 할인 정책 회원은 상품을 주문할 수 있다.
회원 등급에 따라 할인 정책을 적용할 수 있다.

할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있다.)
할인 정책은 변경 가능성이 높다.
회사의 기본 할인 정책을 아직 정하지 못했고,
오픈 직전까지 고민을 미루고 싶다.

최악의 경우 할인을 적용하지 않을 수 도 있다. (미확정)

역할과 구현을 잘 분리해서 확장성 있게 조립가능하도록 만든다.

 

 

 

2. 주문, 할인 도메인 개발

신규 개발 소스

두개의 도메인을 만들어 줄것이다.

먼저 Discount 부분을 진행하자

 

2-1) Discount 도메인

인터페이스명과 클래스명, 해당 위치까지 소스에 나와있으니 따로 적지 않겠다.

package hello.core.discount;

import hello.core.member.Member;

public interface DiscountPolicy {
    //    회원, 금액 -> 할인 금액 반환
    int discount(Member member, int price);
}
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;
    }
}

할인의 역할을 가지는 하는 DiscountPolicy 인터페이스를 생성하고

고정할인 정책의 FixDiscountPolicy 구현체를 하나 만들어주었다. (추후 정율할인도 만들 예정)

 

2-2) Order 도메인

package hello.core.order;

public class Order {
    private Long memberId;
    private String itemName;
    private int itemPrice;
    private int discountPrice;

    public Order(Long memberId, String itemName, int itemPrice, int discountPrice) {
        this.memberId = memberId;
        this.itemName = itemName;
        this.itemPrice = itemPrice;
        this.discountPrice = discountPrice;
    }
    public int calculratePrice(){
        return itemPrice - discountPrice;
    }

    public Long getMemberId() {
        return memberId;
    }

    public String getItemName() {
        return itemName;
    }

    public int getItemPrice() {
        return itemPrice;
    }

    public int getDiscountPrice() {
        return discountPrice;
    }

    @Override
    public String toString() {
        return "Order{" +
                "memberId=" + memberId +
                ", itemName='" + itemName + '\'' +
                ", itemPrice=" + itemPrice +
                ", discountPrice=" + discountPrice +
                '}';
    }
}
package hello.core.order;

public interface OrderService {
    // 주문생성 - 주문자id, 상품명, 상품가격
    Order createOrder(Long memberId, String itemName, int itemPrice);
}
package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
import hello.core.member.MemoryMemberRepository;

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

 

Order 객체는 주문자ID, 상품명, 상품가격, 할인가격을 가지도록 만들어주었다.  (ITEM상품 객체를 안만드신게 찜찜..)

그리고, 할인가격을 계산해주는 calculratePrice를 하나 구현하고

편의를 위해 Getter 와 toString을 재정의 해주었다. 

 

현재 코드는 결합도가 높아 좋지 못하다.

나중에 구현체들이 직접적으로 명시되어 의존된 문제를 어떻게 풀어가시는지 주목하자.

 

2-3) 실행 및 테스트

이전에 MemberApp을 만들어 수행시켜 본 것처럼, 상위 도메인에서 OrderApp을 만들어주자.

package hello.core;

import hello.core.member.*;
import hello.core.order.*;

public class OrderApp {
    public static void main(String[] args) {
        MemberService memberService = new MemberServiceImpl();
        OrderService orderService = new OrderServiceImpl();

        long memberId = 1L;
        Member member = new Member(memberId, "MemberA", Grade.VIP);
        memberService.join(member);

        Order order = orderService.createOrder(memberId, "item1", 10000);
        System.out.println("order = " + order);
    }
}

할인금액을 포함에서 잘 수행된 내역이 나왔다.

테스트 코드도 작성해보자.

package hello.core.order;

import hello.core.member.*;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

public class OrderServiceTest {
    MemberService memberService = new MemberServiceImpl();
    OrderService orderService = new OrderServiceImpl();
    @Test
    void createOrder(){
        long memberId = 1L;
        Member member = new Member(memberId, "MemberA", Grade.VIP);
        memberService.join(member);

        Order order = orderService.createOrder(memberId, "item1", 10000);
        Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
    }
}

테스트가 정상적으로 진행되었다.

 

다음 글에서 이제 새로운 할인 정책을 추가로 개발하고,

연결시키면서 Solid 원리에 무엇이 위배되었는지 생각해보고

지금 설계하고 작성한 코드의 문제점과 해결법에 대해서 학습해보자!

 

 

 

계획을 따르기보다 변화에 대응하기를...!!!!!!

 

이번글까지의 소스코드 ↓