스프링의 핵심기능을 사용하지 않은 상태로 개발을 해보고 객체지향의 원칙(OCP, DIP)을 지키기 위해 스프링 핵심 기능들이 무슨 역할을 하는지 알아보는 시간입니다.
1. 회원 도메인 설계 (회원 가입)
(1) Entity
- 회원(Member)
- 필드 : id, name, grade
- 회원은 Basic과 VIP 두 가지 등급이 있다.
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 Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
}
(2) Repository
- 회원저장소(MemberRepository) (인터페이스)
- save(회원 database에 저장), findById(database에서 회원 찾기)
- 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다.
- MemoryMemberRepository(구현체) 사용
public interface MemberRepository {
void save(Member member);
Member findById(Long memberId);
}
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);
}
}
데이터베이스가 아직 확정이 안되었다. 메모리 회원 저장소를 구현해서 우선 개발 진행.
(3) Service
- 회원 서비스(MemberService)
- 가입(join), 조회(findMember)
public interface MemberService {
void join(Member member);
Member findMember(Long memberId);
}
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
2. 회원 도메인 설계의 문제점
- 의존관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제점이 있다.
public class MemberServiceImpl implements MemberService {
private final MemberRepository(인터페이스) memberRepository = new MemoryMemberRepository()(구현);
위 코드를 보면 MemberServiceImpl Class는 MemberRepository(인터페이스)와 MemoryMemberRepository(구현)에 모두 의존하고 있다.
3. 주문과 할인 도메인 설계
주문 도메인 현력, 역할, 책임
- 주문 생성 : 클라이언트는 주문 서비스에 주문 생성을 요청한다.
- 회원 조회 : 할인을 위해서는 회원 등급이 필요하다. 그래서 주문 서비스는 회원 저장소에서 회원을 조회한다.
- 할인 적용 : 주문 서비스는 회원 등급에 따른 할인 여부를 할인 정책에 위임한다.
- 주문 결과 반환 : 주문 서비스는 할인 결과를 포함한 주문 결과를 반환한다.
(1) Entity
- 주문(Order)
- 필드 : memberId, itemName, itemPrice, discountPrice
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 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 getDiscountPrice() {
return discountPrice;
}
public void setDiscountPrice(int discountPrice) {
this.discountPrice = discountPrice;
}
@Override
public String toString() {
return "Order{" +
"memberId=" + memberId +
", itemName='" + itemName + '\'' +
", itemPrice=" + itemPrice +
", discountPrice=" + discountPrice +
'}';
}
}
(2) Repository
- MemberRepository를 사용한다.
(3) Policy
- DiscountPolicy(인터페이스)
- 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용
- FixDiscountPolicy(구현)
import hello.core.member.Member;
public interface DiscountPolicy {
int discount(Member member, int price);
}
import hello.core.member.Grade;
import hello.core.member.Member;
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;
}
}
(4) Service
- OrderService(인터페이스)
- 주문 생성 요청이 오면, 회원 정보를 조회하고, 할인 정책을 적용한 다음 주문 객체를 생성하고 반환
- 주문생성(createOrder)
- OrderServiceImpl(구현 클래스)
public interface OrderService {
Order createOrder(Long memberId, String itemName, int itemPrice);
}
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;
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);
}
}
4. 회원 도메인 설계의 문제점
- 의존관계가 인터페이스뿐만 아니라 구현까지 모두 의존하는 문제점이 있다.
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
위 코드를 보면 OrderServiceImpl Class는 MemberRepository(인터페이스), DiscountPolicy(인터페이스)와 MemoryMemberRepository(구현), FixDiscountPolicy(구현)에 모두 의존하고 있다.
본 글은 김영한 님의 "스프링 핵심 원리"(인프런) 유료 강의를 들으며 요약, 정리하고 일부 정보를 추가 작성한 것입니다.
저작권 문제시 903yh@naver.com으로 연락 주시면 삭제하겠습니다.
반응형
'Spring(JAVA Framework) > Spring Core' 카테고리의 다른 글
IoC, DI 컨테이너 (0) | 2021.08.15 |
---|---|
스프링 핵심 원리 이해3 (0) | 2021.08.15 |
스프링 핵심 원리 이해2 (0) | 2021.08.15 |
객체 지향 프로그래밍과 설계 원칙(SOLID) (0) | 2021.08.12 |
스프링(Spring Framework)이란? (0) | 2021.08.12 |
댓글