비즈니스 요구사항 설계
회원
- 회원을 가입하고 조회할 수 있다.
- 회원은 일반과 VIP 두 가지 등급이 있다.
- 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정) -> interface
주문과 할인 정책
- 회원은 상품을 주문할 수 있다
- 회원 등급에 따라 할인 정책을 적용할 수 있다.
- 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있다.)
- 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다. (미확정) -> interface
인터페이스를 만들고 구현체를 언제든지 갈아끼울 수 있도록 설계하자!!
회원 도메인 설계(자바로만 구현)
- 회원을 가입하고 조회할 수 있다.
- 회원은 일반과 VIP 두 가지 등급이 있다.
- 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수 있다. (미확정)
Grade (일반과 VIP 존재)
public enum Grade {
BASIC, VIP
}
Member
public class Member {
private Long id;
private String name;
private Grade grade;
생성자 및 getter/setter
}
MemberRepository(Member 저장소)
public interface MemberRepository {
void save(Member member);
Member findById(Long memberId);
}
MemoryMemberRepository(메모리 회원 저장소 구현체)
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);
}
}
데이터베이스에 저장할지 외부와 연동할지 아직 모르므로 일단 메모리 회원 저장소를 구현해서 저장한다.
HashMap 은 동시성 이슈(똑같은 것은 2개 이상 못 받음)가 발생할 수 있다. 이런 경우 ConcurrentHashMap 을 사용하자.
MemberService
public interface MemberService {
void join(Member member);
Member findMember(Long memberId);
}
MemberServiceImpl
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);
}
}
회원 도메인 설계의 문제점
- 이 구현의 문제점은 OCP원칙과 DIP를 지키고 있지 못한다는 것이다.
- 의존관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제점이 있다.
- MemoryMemberRepository() 말고 다른 것을 선택할려면 코드 자체를 바꿔야 하므로 OCP를 지키지 못했다.
- 또 구현체에 의존하고 있으므로 DIP를 지키지 못했다.
주문과 할인 도메인 설계
- 회원은 상품을 주문할 수 있다.
- 회원 등급에 따라 할인 정책을 적용할 수 있다.
- 할인 정책은 모든 VIP는 1000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경 될 수 있다.)
- 할인 정책은 변경 가능성이 높다. 회사의 기본 할인 정책을 아직 정하지 못했고, 오픈 직전까지 고민을 미루고 싶다. 최악의 경우 할인을 적용하지 않을 수 도 있다. (미확정)
회원과 할인 정책을 합쳐서 보면 위 그림의 관계도처럼 그려진다.
각각 역활과 저장소가 분리 된 것을 보면 역할과 구현을 분리해서 자유롭게 구현 객체를 조립할 수 있게 설계했다.
DiscountPolicy(할인 정책 인터페이스)
public interface DiscountPolicy {
int discount(Member member, int price);
}
FixDiscountPolicy(정액 할인 정책 구현체)
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;
}
}
}
VIP이면 1000원 할인
Order(주문 엔티티)
public class Order {
private Long memberId;
private String itemName;
private int itemPrice;
private int discountPrice;
생성자 및 Getter/Setter
}
OrderService(주문 서비스 인터페이스)
public interface OrderService {
Order createOrder(Long memberId, String itemName, int itemPrice);
}
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);
}
}
주문 생성 요청이 오면, 회원 정보를 조회하고, 할인 정책을 적용한 다음 주문 객체를 생성해서 반환한다.
메모리 회원 리포지토리와, 고정 금액 할인 정책을 구현체로 생성한다.
'Spring 이론 공부 > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
좋은 객체 지향 설계의 5가지 원칙의 적용 (0) | 2023.01.28 |
---|---|
관심사의 분리 (1) | 2023.01.28 |
스프링 핵심 원리 이해2 - 객체 지향 원리 적용 (0) | 2023.01.28 |
좋은 객체 지향 설계의 5가지 원칙(SOLID) (0) | 2023.01.27 |
좋은 객체 지향 프로그래밍이란? (0) | 2023.01.27 |