Spring 강의/springMVC

springMVC 기본 - (2) 회원 도메인

lxexjx 2022. 3. 9. 22:04

[도메인 설계]

1. 도메인 협력 관계 : 기획자들도 볼 수 있음.

2. 클래스 다이어그램 : 도메인 협력 관계를 바탕으로 개발자들이 구체화해서 클래스 다이어그램을 만듦.

    실제 서버를 실행하지 않고 클래스들만 분석해서 볼 수 있음.근데 DB뭘 넣을 지는 서버가 뜰때 결정되는 동적.

3. 객체 다이어그램 :  서버가 떠서 클라이언트가 실제로 사용하는 인스턴스끼리의 참조.

역할과 구현을 분리해서 자유롭게 구현 객체를 조립

 

 


 

[회원 도메인 개발]

<enum>

public enum Grade{
  BASIC,
  VIP
}

 

<Member>

public class Member{
   private Long idl
   private String name;
   private Grade grade;
   
   public member(Long id, Strong name, Grade grade){
   this.id = id;
   this.name = name;
   this.grade = grade;
   
    ~get, set으로 private 값 세팅할 수 있도록~   
}

 

<MemberRepository> - (회원 저장소) 리포지터리 인터페이스

public interface MemberRepository {
    void save(Member member);   //저장 기능
    Member findById(Long memberId); //찾는 기능
}

 

<MemoryMemberRepository> - 리포지터리 구현체

@Component

//MemberRepository 구현체
public class MemoryMemberRepository implements  MemberRepository{

    //저장소 만들기
   private  static Map<Long,Member> store = new HashMap<>();

    @Override
    public void save(Member member) {
        store.put(member.getId(), member);
    }

    //저장소에서 가져와서 id찾기
    @Override
    public Member findById(Long memberId) {
        return store.get(memberId);
    }
}

cf. private  static Map<Long,Member> store = new HashMap<>();

여러곳에서 store에 접근하면 동시성의 문제가 있을 수 있어 ConcurrentHashMap을 사용권함

 

 

<MemberService> - 인터페이스

public interface MemberService {
    void join(Member member);   //가입
    Member findMember(Long memberId);   //찾기
}

 

<MemberServiceImpl> - 구현체

public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;   //가입하고 찾기 위해->생성자 만들고

    public void join(Member member) {
        memberRepository.save(member);  //-> 다형성에 의해 인터페이스(memberRepository)가 아니라 
                                       //MemoryMemberRepository의 save()가 호출됨.
    }

    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

private final MemberRepository memberRepository = new MemoryMemberRepository;

구현체 없이 null 이면 exception-> 구현 객체 선택

다형성에 의해 인터페이스가 아니라 MemoryMemberRepository에 있는 save가 호출(추상화와 구현 모두에 의존..그러면 안됨)

 


<MemberApp>

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 FindMemner = memberService.findmember(1L):
        }
}

<MemberApp> 은 스프링 없이 순수 자바로 테스트 -> jUnit를 사용함 : <MemberServiceTest>

 


<MemberServiceTest>

join한거랑 찾은거랑 같은지 눈으로 출력하는 system.out이 아닌 Assert통해서 빠르게 확인가능 

public class MemberServiceTest {
    MemberService memberService = new MemberServiceImpl();

    @BeforeEach
    public  void beforeEach(){
        AppConfig appConfig=new AppConfig();
        memberService= appConfig.memberService();
    }
    @Test
    void join(){
        //given ~주어졌을때
        Member member=new Member(1L,"membera",Grade.VIP);
        //when ~이렇게 했을 때
        memberService.join(member);
        Member findMember=memberService.findMember(1L); //찾아서 then에서 검증
        //then ~이렇게 된다  
        Assertions.assertThat(member).isEqualTo(findMember);
    }
}

여기까진 DI위반 중


 

[주문과 할인 도메인]

객체 다이어그램 : 실제로 new해서  생성하는 application띄워서 동적으로 객체들의 연관관계 맺어짐.

 

 

 

<DiscountPolicy> - 인터페이스

public interface DiscountPolicy {
	//return:할인대상금액
    int discount(Member member,int price);
}

 

<FixDiscountPolicy> - 정액할인 구현체

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>

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;
    }

    ~Getter&Setter 작성

    //객체를 출력하면 toString으로 나와
    @Override
    public String toString() {
        return "Order{" +
                "memberId=" + memberId +
                ", itemName='" + itemName + '\'' +
                ", itemPrice=" + itemPrice +
                ", discountPrice=" + discountPrice +
                '}';
    }
}

 

<OrderService> - 주문서비스 (주문 결과 반환)인터페이스

public interface OrderService {
    Order createOrder(Long memberId, String itemName, int itemPrice);  
     //주문생성시 파라미터를 넘겨 return으로 반환한다.
}

 

<OrderServiceImpl> - 주문서비스(주문 결과 반환) 구현체

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);
    }
}

주문생성 요청이 오면 회원정보를 먼저 조회하고 할인정책에 회원을 넘겨서 최종 생성된 주문을 반환

OrderService는 할인에 대해 몰라 discountPolicy가 알아서해줘~->단일체계 원칙을 잘 지킴

 

 

<OrderApp>

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);
    }
}

주문테스트 main메서드에서 테스트함. 스프링 없이 순수 자바로 테스트 -> jUnit를 사용함 <OrderServiceTest>

 

 

<OrderServiceTest> - 주문테스트 자동화된 테스트

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.getDiscountPrice()).isEqualTo(1000);	//검증!
    }
}