본문 바로가기
Spring/Spring Framework(Basic)

[Spring] 순수 자바 코드를 이용해 Service 구축하기 - 2

by 완두완두콩 2021. 10. 27.

 

복습


저번 포스트에서는 회원 객체를 생성하고 , 로컬 Repository에 저장하기 위한 메소드와

이를 처리하기 위한 MemberService 를 정의하였다.이번 포스트에서는 저장과 할인 정책을 개발해본다.

 

요구사항


 

회원

회원을 가입하고 조회할 수 있다.

회원은 일반과 VIP 두 가지 등급이 있다.

회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)

 

주문과 할인 정책

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

할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있다.)

할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다.

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

 

<주문 도메인 다이어그램>

 

1
2
3
4
5
6
7
8
9
package hello.core.discount;
 
import hello.core.member.Member;
 
public interface DiscountPolicy {
 
    int discount(Member member , int price);
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
        }
    }
}
 
cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package hello.core.discount;
 
import hello.core.member.Grade;
import hello.core.member.Member;
 
public class RateDiscountPolicy implements DiscountPolicy{
 
    private int discountPercent = 10;
 
    @Override
    public int discount(Member member, int price) {
        if(member.getGrade() == Grade.VIP){
            return price * discountPercent / 100;
        } else{
            return 0;
        }
    }
}
 
cs

 

-> 정률할인 정책과 고정 할인 정책 개발. Grade.VIP  일때만 할인이 들어간다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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 Long getMemberId() {
        return memberId;
    }
 
    public void setMemberId(Long memberId) {
        this.memberId = memberId;
    }
 
    public String getItemName() {
        return itemName;
    }
 
    public void setItemName(String itemName) {
        this.itemName = itemName;
    }
 
    public int getItemPrice() {
        return itemPrice;
    }
 
    public void setItemPrice(int itemPrice) {
        this.itemPrice = itemPrice;
    }
 
    public int getDiscountPrice() {
        return discountPrice;
    }
 
    public void setDiscountPrice(int discountPrice) {
        this.discountPrice = discountPrice;
    }
}
 
cs

 

 

1
2
3
4
5
6
package hello.core.order;
 
public interface OrderService {
    Order createOrder(Long memberId, String itemName , int itemPrice);
}
 
cs

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package hello.core.order;
 
import hello.core.Repository.MemberRepository;
import hello.core.Repository.MemoryMemberRepository;
import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.Member;
 
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);
    }
}
 
cs

 

-> 주문에 관련된 로직 작성.

 

-> 할인 정책을 바꾸기 위해서는 Fix 또는 Rate 로 DiscountPolicy 를 바꾸어 주면 된다.

-> 각각의 Fix , Rate 라는 역할과 이를 구현한 객체들은 모두 분리돼있다.

-> 그렇지만 객체지향설계의 원칙을 지키지는 못했다.

 

<객체지향설계 원칙 위반>

 

-> OCP 위반 : 코드를 변경하지 않고 확장 가능해야 하는데 , 할인 정책이 바뀔때마다 직접 Fix --> Rate 로 변경이 일어난다.

-> DIP 위반 : OrderServiceImpl 은 추상 객체인 인터페이스 DiscountPolicy 뿐만 아니라 Fix, Rate 의 구체 클래스에도 의존한다. 

- OrderServiceImpl 내의 코드 : 

private final DiscountPolicy discountPolicy = new FixDiscountPolicy();

이는 추상과 구현 둘다 의존. 그렇지 않기 위해서는

private final DiscountPolicy discountPolicy ;

로 끝나야 한다.  만약 private final DiscountPolicy discountPolicy  처럼 바꾼다면 discountPolicy 는 어느 객체를 의존하고 있는지 알 수 없다. ( 해당 코드를 실행하면 Null Pointer Exception 발생)

※ 이를 해결하기 위해서 '의존성 주입 DI' (Dependency Injection) 이 필요하다.

 

 

- 다음 장에서는 의존성 주입에 대해 알아보고, 개발해보도록 하겠다.

 

* 해당 글은 'Infleran'의 김영한 강사님의 자료를 참조하였습니다.

댓글