1. 주문 생성: 클라이언트는 주문 서비스에 주문 생성을 요청한다. (클라이언트는 controller 같은거라 생각하면됨)
-> 회원아이디,상품명,상품가격을 같이 넘긴다.
2. 회원 조회: 할인을 위해서는 회원 등급이 필요하다. 그래서 주문 서비스는 회원 저장소에서 회원을
조회한다. (등급을 받아와서 할인을 해야하는지 판단해야해서)
3. 할인 적용: 주문 서비스는 회원 등급에 따른 할인 여부를 할인 정책에 위임한다. (할인 정책한테 할인 해도되는건지 확인하는 부분)
4. 주문 결과 반환: 주문 서비스는 할인 결과를 포함한 주문 결과를 반환한다.
역할과 구현을 분리해서 구현하려고 설계 (중요)
역할과 구현을 분리해서 자유롭게 구현 객체를 조립할 수 있게 설계했다.
덕분에 회원 저장소는 물론이고, 할인 정책도 유연하게 변경할 수 있다.
주문 도메인 클래스 다이어그램
동적으로 어떤 구현체를 선택할건지 표시하는 객체 다이어그램
주문 도메인 객체 다이어그램2
리파지토리 구현체와 할인 정책 구현체가 바뀌더라도 주문서비스 구현체를 바꿀 필요가 없다. (역할과 구현을 나눠서 설계를 해놨기 때문에)
주문과 할인 도메인 개발
DiscountPolicy.java (인터페이스)
package com.example.demo.discount;
import com.example.demo.member.Member;
public interface DiscountPolicy {
/**
*
* @param member
* @param price
* @return 할인 대상 금액
*/
int discount(Member member,int price);
}
단축키팁
1. F2를 누르면 오류난곳으로 이동된다. 알트+엔터해야하는곳으로
2. 메소드 위에서 /** 치고 컨트롤+쉬프트+엔터를 하면 파람값은 자동으로 적혀있고 , 리턴값을 적게끔 구성된 주석이 생긴다. -> 위 코드 주석처럼
FixDiscountPolicy.java (구현체)
package com.example.demo.discount;
import com.example.demo.member.Grade;
import com.example.demo.member.Member;
public class FixDiscountPolicy implements DiscountPolicy {
private int discountFixAmount = 1000; //1000원 할인
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.VIP) { // enum타입은 == 써서 비교하는게 맞다.
return discountFixAmount;
}else{
return 0;
}
}
}
Order.java ( 주문 엔티티)
package com.example.demo.order;
public class Order {
private Long memberId;
private String itemName;
private int itemPrice;
private int disConntPrice;
public Order(Long memberId, String itemName, int itemPrice, int disConntPrice) {
this.memberId = memberId;
this.itemName = itemName;
this.itemPrice = itemPrice;
this.disConntPrice = disConntPrice;
}
//최종계산 금액 리턴해주는 함수 생성
public int calculatePrice() {
return itemPrice - disConntPrice;
}
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 getDisConntPrice() {
return disConntPrice;
}
public void setDisConntPrice(int disConntPrice) {
this.disConntPrice = disConntPrice;
}
//출력을 쉽게 확인하려고 toString()까지 추가
@Override
public String toString() {
return "Order{" +
"memberId=" + memberId +
", itemName='" + itemName + '\'' +
", itemPrice=" + itemPrice +
", disConntPrice=" + disConntPrice +
'}';
}
}
toString()에 구현된 메소드는
System.out.println("order = " + order);
와같이 했을때 order를 그냥 출력했을때 저 메소드가 실행되서 출력됨
@Getter 등, 롬복 안사용하고 강의대로 따라해봄
OrderService.java(인터페이스)
package com.example.demo.order;
public interface OrderService {
//주문생성 메소드 (리턴으로 주문결과를 반환)
Order createOrder(Long memberId, String itemName, int itemPrice);
}
OrderServiceImpl.java(구현체)
package com.example.demo.order;
import com.example.demo.discount.DiscountPolicy;
import com.example.demo.discount.FixDiscountPolicy;
import com.example.demo.member.Member;
import com.example.demo.member.MemberRepository;
import com.example.demo.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);
}
}
주문 생성 요청이 오면, 회원 정보를 조회하고, 할인 정책을 적용한 다음 주문 객체를 생성해서 반환한다.
메모리 회원 리포지토리와, 고정 금액 할인 정책을 구현체로 생성한다.
int discountPrice = discountPolicy.discount(member, itemPrice);
주문관련 코드에서는 할인관련 코드를 몰라도 되고, 할인관련 코드의 수정이 있어도 관련없다. (단일 체계원칙 잘지킨것)
주문과 할인 도메인 실행과 테스트
오류발생
static으로 해둔것은 클래스가 메모리에 올라갈때 자동적으로 생성된다. + 객체 공유
private static Map<Long, Member> store = new HashMap<>();
OrderApp.java (클라이언트 테스트)
package com.example.demo;
import com.example.demo.member.Grade;
import com.example.demo.member.Member;
import com.example.demo.member.MemberService;
import com.example.demo.member.MemberServiceImpl;
import com.example.demo.order.Order;
import com.example.demo.order.OrderService;
import com.example.demo.order.OrderServiceImpl;
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, "itemA", 10000);
System.out.println("order = " + order);
}
}
createOrder 주문생성 테스트
OrderServiceTest.java 테스트생성
package com.example.demo.order;
import com.example.demo.member.Grade;
import com.example.demo.member.Member;
import com.example.demo.member.MemberService;
import com.example.demo.member.MemberServiceImpl;
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, "itemA", 10000);
Assertions.assertThat(order.getDisConntPrice()).isEqualTo(1000);
}
}
createOrder() 테스트를 작성할때 필요한것들을 생각해서 객체를 생성한다.
테스트니까 테스트용 멤버를 생성해야하므로 멤버서비스 구현체 객체를 생성하고 (회원가입에 써야하니까)
createorder()메소드를 써야하니까 오더서비스구현체 객체를 생성하고
결과값을 체크해야하니까 Assertions를 이용해서 값비교를 한다.
단위테스트는 중요하다
스프링이나 컨테이너 같은거 없이 , 순수한 자바코드로 테스트하는것 == 단위테스트 -> 중요하다 (스프링 띄우고 하는 테스트는 상당히 느림) (단위테스트는 상당히 빠름)
'인프런 > 스프링핵심원리(기본)' 카테고리의 다른 글
7) ★ 관심사의 분리, AppConfig(리팩토링 전) , 생성자 주입,의존성 주입(의존관계 주입) (0) | 2022.12.09 |
---|---|
6)새로운 할인 정책 개발 , 적용과 문제점 , 문제점 해결 (의존성 주입) (0) | 2022.12.09 |
4)회원 도메인 설계,회원 도메인 개발 (0) | 2022.12.01 |
3)예제프로젝트 시작, 비즈니스 요구사항과 설계 (0) | 2022.12.01 |
2)좋은 객체 지향설계의 5가지 원칙(SOLID), 객체지향 설계와 스프링 (0) | 2022.12.01 |
댓글