반응형

스프링 핵심 원리 이해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원 할인

 

  1. 클라이언트 - (주문생성) -> 주문 서비스 역할 - (회원조회) -> 회원저장소 역할

    주문생성(id, 상품명, 상품가격), 회원조회(id로 조회) => 상품명과 상품가격은 간단하게 객체가 아니라 data로 만듬

  2. 주문서비스 역할 - (할인 적용) -> 할인 정책역할

    할인 적용 ( vip인가? 확인)

  3. 주문서비스 역할 - (주문결과 반환) -> 클라이언트

    주문결과는 간단하게 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);
    }
}

 

 

728x90
반응형
블로그 이미지

아상관없어

,