본 게시글은 김영한님의 스프링 핵심 원리 기본편을 정리한 글입니다.
위 그림에서와 같이 일단 역할을 부여하였고 이에 대한 구현 부분에 대해서 어떻게 설계를 해야 할 지 그림으로 확인해보자.
이렇게 할 경우 유동적으로 나중에 정책이나 저장소가 변경이 되었을 경우 유연하게 변경을 할 수 있게 된다.
그렇게 되면 클래스, 객체에 대한 다이어그램을 확인해 보면
이렇게 유연하게 변경이 될 수 있다는것을 알 수 있다.
그럼 이제 본격적으로 구현작업을 해보자.
public interface DiscountPolicy {
int discount(Member member, int itemPrice);
}
public class FixDiscountPolicy implements DiscountPolicy{
private int discountAmount=1000;
@Override
public int discount(Member member, int itemPrice) {
if (member.getGrade() == Grade.VIP){
return discountAmount;
}
else{
return 0;
}
}
}
package practicecore.ptcore.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 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 Long getMemberId() {
return memberId;
}
public void setMemberId(Long memberId) {
this.memberId = memberId;
}
public int getDiscountPrice() {
return discountPrice;
}
public void setDiscountPrice(int discountPrice) {
this.discountPrice = discountPrice;
}
@Override
public String toString() {
return "Order{" +
"itemName='" + itemName + '\'' +
", itemPrice=" + itemPrice +
", memberId=" + memberId +
", discountPrice=" + discountPrice +
'}';
}
}
public interface OrderService {
Order createOrder(Long memberId, String itemName, int itemPrice);
}
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 discountP=discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountP);
}
}
public class OrderServiceTest {
MemberService memberService = new MemberServiceImpl();
OrderService orderService = new OrderServiceImpl();
@Test
void create(){
Long memberId=1L;
Member member= new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
System.out.println("member.getGrade() = " + member.getGrade());
Order order = orderService.createOrder(memberId, "itemA", 10000);
Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
}
}
앞에서 나온 코드를 보게 되면 memorymemberrepository에 저장을 하기위하여 MAP을 선언하였던 것을 기억하는가?
앞선 블로그에서는 static이 빠진 아래의 코드로 처음에는 넣었었다.
private final Map<Long, Member> store = new HaspMap<>();
그랬더니 NPE가 터지면서 Discount쪽 문제가 생겼다고 한다. 그러나....
기본기가 너무나도 부족하였던 나 자신을 발견하게 되었다.
//private final Map<Long, Member> store = new HaspMap<>();
private static final Map<Long, Member> store = new HaspMap<>();
위 코드와 달리 밑에는 static이 들어가게 되었다.
static을 붙이지 않아서 일어났었는데 일단 오류에 대한 분석을 해보자면 다음과 같다.
public class OrderServiceTest {
MemberService memberService = new MemberServiceImpl();
OrderService orderService = new OrderServiceImpl();
@Test
void create(){
Long memberId=1L;
Member member= new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
memberService.join, orderService.createOrder 두 가지의 store는 서로 다른 친구이다. 그래서 static을 붙여줌으로써 store가 각각의 인스턴스에게 부여해주는 것을 방지하고 하나의 store만 존재할 수 있도록 하는 작업이 필요하였다.
생각보다 이 문제 잘 못찾고 있어서 되게 고생하고 있었다. ㅠㅠㅠ
주문 할인 서비스까지는 잘 개발을 하였나? 라고 한다면 어긴 법칙들이 있다. OCP, DIP인데 MemberRepository memberRepository = new MemoryMemberRepository(); DiscountPolicy discountPolicy = new FixDiscountPolicy(); 에서 보게 되면 DIP를 어긴 것이 주로 추상체에 집중을 해야하는데 의존하는것이 구현체까지 클라이언트 코드에서 신경을 써줘야 한다는 것이 보인다. 그리고 만약 할인정책을 변경하게 된다면 FixDiscountPolicy라는 코드 자체를 변경해야하므로 OCP도 어기게 되는 부분임을 알 수 있다. 그렇다면 이러한 문제들은 어떻게 해결해야할 것인지 다음 글에서 하나씩 알아보자.