스프링 핵심 원리 이해1 - 예제
비지니스 요구사항과 설계
회원
회원 가입, 조회 가능
일반, vip 등급의 회원 존재
DB는 미정
주문과 할인 정책
회원이 상품 주문가능
등급에 따라 할인 정책 가능
VIP는 무조건 1000원 할인 (정책 가정, 확정은 아님)
=> 미정인 부분은 인터페이스로 역할/구현을 분리하면 된다!!
** 일단은 스프링이 아닌 순수 자바코드로 구현한다고 가정
회원 도메인
- 클라이언트 -> 회원서비스(가입, 조회) -> 회원 저장소(미정)
- MemberService(구현체: MemberServiceImpl) -> MemberRepository(구현체: 일단 MemoryMemberRepository)
- 클라이언트 객체 -> 회원서비스 객체(MemberServiceImpl) -> 멤버리포지토리 객체
회원 도메인 개발
회원 등급, 회원 엔티티 필요
회원등급
package hello.core.member;
public enum Grade {
BASIC,
VIP
}
회원 엔티티
package hello.core.member;
//id, name, grade 필요
public class Member {
private long id;
private String name;
private Grade grade;
public Member(long id, String name, Grade grade) {
this.id = id;
this.name = name;
this.grade = grade;
}
public void setId(long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public Grade getGrade() {
return grade;
}
}
회원 저장소
회원저장소 인터페이스, 구현체
회원저장소 인터페이스 - 저장, 멤버찾기 기능
package hello.core.member;
//id, name, grade 필요
public class Member {
private long id;
private String name;
private Grade grade;
public Member(long id, String name, Grade grade) {
this.id = id;
this.name = name;
this.grade = grade;
}
public void setId(long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public Grade getGrade() {
return grade;
}
}
구현체 - 일단 메모리 저장소
package hello.core.member;
import java.util.HashMap;
import java.util.Map;
//저장소 필요
public class MemoryMemberRepository implements MemberRepository{
//일단 임시 저장소 = 메모리 사용
private static Map<Long, Member> store = new HashMap<>();
@Override
public void save(Member member) {
store.put(member.getId(),member);
}
@Override
public Member findById(Long memberId) {
return store.get(memberId);//store는 Map이므로 키값으로 member찾음
}
}
회원 서비스
회원 가입과 조회 기능이 필요하다.
회원 서비스 인터페이스 (역할)
package hello.core.member;
public interface MemberService {
void join(Member member);
Member findMember(Long memberId);
}
구현
package hello.core.member;
//멤버서비스 객체는 멤버리포지토리 객체가 필요함
public class MemberServiceImpl implements MemberService{
MemberRepository memberRepository = new MemoryMemberRepository();
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
일단 멤버도메인, 멤버서비스 ,멤버저장소 구현완료!!
일단 main으로 테스트
package hello.core.member;
//테스트해보는 용도
public class MemberApp {
public static void main(String[] args) {
MemberService memberService = new MemberServiceImpl();
Member member = new Member(1l, "memberA", Grade.VIP);
//회원가입
memberService.join(member);
//가입한 멤버가 있는지 조회
Member findMember = memberService.findMember(1L);
//확인
System.out.println("new member = " + member.getName());
System.out.println("findMember = " + findMember.getName());
}
}
은 안좋으니 Junit으로 테스트
package hello.core;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
public class MemberServiceTest {
MemberService memberService = new MemberServiceImpl();
@Test
void join(){
//given - vip
Member member = new Member(1L, "memberA" , Grade.VIP);
//when - 회원가입
memberService.join(member);
Member findMember = memberService.findMember(member.getId());
//then - 가입한 회원이 맞는지
Assertions.assertThat(member).isEqualTo(findMember);
}
}
=> memberServiceImpl에서 MemberRepository memberRepository = new MemoryMemberRepository(); 로 구현체를 직접 참조하고 있다. 따라서 구현에 의존하게 되는 문제점이 발생한다.!
주문과 할인 도메인 설계
- 회원은 상품 구매가능
- 등급에따라 할인됨
- 할인정책으론 모든 vip는 1000원 할인
클라이언트 - (주문생성) -> 주문 서비스 역할 - (회원조회) -> 회원저장소 역할
주문생성(id, 상품명, 상품가격), 회원조회(id로 조회) => 상품명과 상품가격은 간단하게 객체가 아니라 data로 만듬
주문서비스 역할 - (할인 적용) -> 할인 정책역할
할인 적용 ( vip인가? 확인)
주문서비스 역할 - (주문결과 반환) -> 클라이언트
주문결과는 간단하게 DB에 저장이 아니라 주문결과를 반환
클라이언트 -> 주문서비스 -> 1. 회원저장소 2.할인정책
할인 정책에는 고정가 할인 정책과 비율 할인 정책이 있다.
할인 정책 인터페이스
package hello.core.discount;
import hello.core.member.Member;
//vip 인지 확인해서 할인된 상품가격 결과를 반환한다.
public interface DiscountPolicy {
/**
* @return 할인 대상 금액
*/
int discount(Member member, int price);
}
고정가 할인 정책
package hello.core.discount;
import hello.core.member.Grade;
import hello.core.member.Member;
//vip이면 1000원 할인, 할인금액 반환
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;
}
}
}
주문 서비스
먼저 주문 엔티티를 만든다.
(회원 id, 상품명, 상품가격)
package hello.core.order;
//주문 엔티티 => 회원id, 상품명, 상품가격, 할인된 금액
//할인된 결과 필요
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 calculatePrice(){
return itemPrice - discountPrice;
}
public void setMemberId(Long memberId) {
this.memberId = memberId;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public void setItemPrice(int itemPrice) {
this.itemPrice = itemPrice;
}
public void setDiscountPrice(int discountPrice) {
this.discountPrice = 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;
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;
//주문을 생성해서 저장소에 vip인지 조회
//vip이면 할인 해줌
public class OrderServiceImpl implements OrderService{
//저장소 조회
private final MemberRepository memberRepository = new MemoryMemberRepository();
//할인을 위해서 할인 정책 필요
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
//id로 조회해서 vip 이면 할인정책 적용
//주문 엔티티 => 회원id, 상품명, 상품가격, 할인된 금액
@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);
}
}
주문과 할인 테스트
package hello.core.order;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import net.minidev.json.JSONUtil;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
//회원등급을 조회하고 그에맞는 할인이 적용되었는가?
//주문서비스 -> 저장소, 할인정책
public class OrderServiceTest {
//given
MemberService memberService = new MemberServiceImpl();
OrderService orderService = new OrderServiceImpl();
//주문 테스트
@Test
void createOrder() {
Long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
//when
memberService.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
//then
Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
}
}
'공부 > Spring_2-스프링 핵심 원리 이해1' 카테고리의 다른 글
스프링 핵심 원리 이해1_6 - 싱글톤 컨테이너 (0) | 2022.04.07 |
---|---|
스프링 핵심원리 이해1_5 - 다양한 설정 형식 지원(자바코드, xml) (0) | 2022.04.07 |
스프링 핵심원리 이해1_4 - 스프링컨테이너 빈 조회 (0) | 2022.04.07 |
스프링 핵심원리 이해1_3 - 스프링으로 전환(AppConfig 기반) (0) | 2022.04.07 |
스프링 핵심원리 이해1_2 - 새로운 할인정책 개발, AppConfig (0) | 2022.04.07 |