티스토리 뷰
회원가입 시스템 설계(도메인 - member, order, config)
enum 생성
회원 등급(BASIC / VIP)
package member;
public enum Grade {
BASIC, VIP
}
Member 클래스 생성
package member;
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;
}
// getter, setter
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;
}
// toString
@Override
public String toString() {
return "Member [id=" + id + ", name=" + name + ", grade=" + grade + "]";
}
}
MemberRepository 인터페이스 생성
package member.repository;
import member.Member;
public interface MemberRepository {
void save(Member member);
Member findById(Long memberId);
}
MemeoryMemberRepositoryImpl 클래스 생성
싱글톤 패턴
MemberRepository 인터페이스를 구현하는 구현 클래스
package member.repository;
import java.util.HashMap;
import java.util.Map;
import member.Grade;
import member.Member;
public class MemoryMemberRepositoryImpl implements MemberRepository {
// 싱글톤
private static MemoryMemberRepositoryImpl instance;
private Map<Long, Member> memberStore = new HashMap<>(); // 자료구조 메모리 공간만 생성
// 기본 생성자(객체가 인스턴스화 시 생성자 먼저 호출)
private MemoryMemberRepositoryImpl() {
initData();
}
// 싱글톤 외부 접근 메서드(외부에서 인스턴스 주소를 반환 받을 수 있도록 함)
public static MemoryMemberRepositoryImpl getInstance() {
if(instance == null) {
instance = new MemoryMemberRepositoryImpl();
}
return instance;
}
// 샘플 초기 데이터
private void initData() {
//Map<Long, Member>의 put
memberStore.put(1L, new Member(1L, "홍길동", Grade.VIP)); // L은 접미사
memberStore.put(2L, new Member(2L, "이몽룡", Grade.BASIC));
memberStore.put(3L, new Member(3L, "성춘향", Grade.VIP));
}
@Override
public void save(Member member) {
// 회원 저장 기능
memberStore.put(member.getId(), member);
}
@Override
public Member findById(Long memberId) {
return memberStore.get(memberId);
}
}
DBMemberRepositoryImpl 클래스 생성
MemberRepository 인터페이스를 구현하는 구현 클래스
package member.repository;
import member.Member;
public class DBMemberRepositoryImpl implements MemberRepository{
@Override
public void save(Member member) {
}
@Override
public Member findById(Long memberId) {
return null;
}
}
MemberService 인터페이스 생성
package member.service;
import member.Member;
public interface MemberService {
void signUp(Member member);
Member findByIdMember(Long memberId);
}
MemberServiceImpl 클래스 생성
MemberService 인터페이스 구현 클래스
package member.service;
import member.Member;
import member.repository.MemberRepository;
public class MemberServiceImpl implements MemberService {
private MemberRepository repository;
// 생성자 의존 주입 DI
public MemberServiceImpl(MemberRepository memberRepository) {
this.repository = memberRepository;
}
@Override
public void signUp(Member member) {
repository.save(member);
}
@Override
public Member findByIdMember(Long memberId) {
return repository.findById(memberId);
}
}
MemberServiceImpl 클래스는 MemberRepository 인터페이스로부터 의존성 주입을 받음
※ 생성자 의존 주입 DI(Dependency Injection)
- 객체지향 프로그래밍에서 객체의 생성자를 통해 필요한 객체(의존성)을 주입하는 방식
- 의존성을 외부에서 주입 받아 클래스 내부에서 필요한 객체를 직접 생성하지 않고, 외부에서 제공된 객체를 사용하는 설계 방식
- 주입된 객체(의존성)은 해당 클래스에서 사용하는 서비스나 테이터이다.
개념
- 의존성: 클래스가 다른 객체를 필요로 하는 객체(객체 생성 시 반드시 주입되어야 함)
- 주입: 필요한 객체를 클래스 외부에서 제공하는 것
- 생성자: 생성자를 통해 외부에서 제공된 의존성을 주입 받아 초기화
장점
- 객체 간 결합도 낮춤: 클래스가 스스로 객체 생성X -> 결합도 낮아져 유연하고 재사용 가능한 코드 작성 가능
- 테스트 용이성: 의존성을 외부에서 주입 받으므로 테스트 시 의존성 쉽게 교체 가능
- 불변성 유지: 주입된 의존성은 변경되지 않음
테스트
package member;
import member.repository.DBMemberRepositoryImpl;
import member.repository.MemberRepository;
import member.repository.MemoryMemberRepositoryImpl;
import member.service.MemberService;
import member.service.MemberServiceImpl;
public class MainTest1 {
public static void main(String[] args) {
MemberRepository memberRepositoryDB = new DBMemberRepositoryImpl();
MemberRepository memberRepository = MemoryMemberRepositoryImpl.getInstance(); // 싱글톤 패턴
MemberService memberService = new MemberServiceImpl(memberRepository); // DI - 객체 주입
// 클라이언트
Member member = new Member(100L, "티모", Grade.VIP); //member 객체 생성
// 신규 회원 가입 기능 요청
memberService.signUp(member);
// 정상적으로 메모리에 들어갔는지 확인
System.out.println(memberService.findByIdMember(100L));
System.out.println(memberService.findByIdMember(1L));
System.out.println(memberService.findByIdMember(2L));
System.out.println(memberService.findByIdMember(3L));
}
}
실행결과
주문과 할인 정책
Order 클래스 생성
package 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) {
super();
this.memberId = memberId;
this.itemName = itemName;
this.itemPrice = itemPrice;
this.discountPrice = discountPrice;
}
// 상품 금액의 할인된 가격 금액 출력하는 기능
public int calculateDiscount() {
return itemPrice - discountPrice;
}
// getter, setter
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;
}
// toString
@Override
public String toString() {
return "Order [memberId=" + memberId + ", itemName=" + itemName + ", itemPrice=" + itemPrice
+ ", discountPrice=" + discountPrice + "]";
}
}
DiscountPolicy 인터페이스 생성
package order;
import member.Member;
public interface DiscountPolicy {
int discount(Member member, int price);
}
FixDiscountPolicyImpl 클래스 생성
DiscountPolicy 인터페이스 구현 클래스
package order;
import member.Grade;
import member.Member;
public class FixDiscountPolicyImpl implements DiscountPolicy {
// 할인 고정 금액: 1500원
private int discountFixAmount = 1500;
@Override
public int discount(Member member, int price) {
// VIP 회원만 할인 금액 적용
if(member.getGrade() == Grade.VIP) {
return discountFixAmount;
}
return 0;
}
}
PercentDiscountPolicyImpl 클래스 생성
DiscountPolicy 인터페이스 구현 클래스
package order;
import member.Grade;
import member.Member;
public class PercentDiscountPolicyImpl implements DiscountPolicy {
// 할인 퍼센트: 10%
private double discountPercent = 10.0;
@Override
public int discount(Member member, int price) {
// VIP 회원만 할인 금액 적용
if(member.getGrade() == Grade.VIP) {
return (int)(price * (discountPercent / 100)); // 10% 할인된 금액
}
return 0;
}
}
OrderService 인터페이스 생성
package order;
public interface OrderService {
Order createOrder(Long memberId, String itemName, int itemPrice);
}
OrderServiceImpl 클래스 생성
OrderService 인터페이스를 구현한 구현 클래스
package order;
import member.Member;
import member.repository.MemberRepository;
public class OrderServiceImpl implements OrderService {
// 컴파일러가 기본 생성자를 뽑아줌 -> 초기화 보장X
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
// 생성자 의존 주입 DI
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
// 주문 생성
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId); // 회원 Id 조회
int discountPrice = discountPolicy.discount(member, itemPrice); // 할인된 금액
return new Order(memberId, itemName, itemPrice, discountPrice); // 주문 생성(객체 생성 및 생성자 호출)
}
}
테스트 1
OrderServiceTest
package order;
import member.Grade;
import member.Member;
import member.repository.MemberRepository;
import member.repository.MemoryMemberRepositoryImpl;
public class OrderServiceTest {
public static void main(String[] args) {
//MemoryMemberRepositoryImpl는 싱글톤 -> new X
MemberRepository memberRepository = MemoryMemberRepositoryImpl.getInstance();
Member member = new Member(1L, "홍길동", Grade.VIP);
// 할인 정책 테스트(고정 / 퍼센트)
DiscountPolicy fixDiscountPolicy = new FixDiscountPolicyImpl();
DiscountPolicy percentDiscountPolicy = new PercentDiscountPolicyImpl();
// 1. OrderService orderService1 = new OrderServiceImpl(memberRepository, fixDiscountPolicy);
// 2. OrderService orderService1 = new OrderServiceImpl(memberRepository, percentDiscountPolicy);
Order order1 = orderService1.createOrder(1L, "맥북", 10000);
System.out.println(order1);
System.out.println(order1.calculateDiscount());
}
}
1번 실행 결과(고정 금액 할인)
memberId가 1인 홍길동은 맥북 10000원을 1500원(고정 금액) 할인 받아서
할인 후 가격이 8500원임을 알 수 있다.
2번 실행결과(퍼센트 할인)
memberId 가 1인 홍길동은 맥북 10000원을 10% 할인(1000원) 받아서
할인 후 가격이 9000원임을 알 수 있다.
테스트 2
OrderServiceTest
package order;
import member.Grade;
import member.Member;
import member.repository.MemberRepository;
import member.repository.MemoryMemberRepositoryImpl;
public class OrderServiceTest {
public static void main(String[] args) {
//MemoryMemberRepositoryImpl는 싱글톤 -> new X
MemberRepository memberRepository = MemoryMemberRepositoryImpl.getInstance();
Member member = new Member(1L, "홍길동", Grade.VIP);
// 할인 정책 테스트(고정 할인 금액)
DiscountPolicy fixDiscountPolicy = new FixDiscountPolicyImpl();
DiscountPolicy percentDiscountPolicy = new PercentDiscountPolicyImpl();
OrderService orderService1 = new OrderServiceImpl(memberRepository, fixDiscountPolicy);
Order order1 = orderService1.createOrder(1L, "맥북", 10000);
System.out.println(order1);
System.out.println(order1.calculateDiscount());
}
}
AppConfig
return type은 인터페이스
return 값은 인터페이스의 구현 클래스
package config;
import member.repository.MemberRepository;
import member.repository.MemoryMemberRepositoryImpl;
import member.service.MemberService;
import member.service.MemberServiceImpl;
import order.DiscountPolicy;
import order.FixDiscountPolicyImpl;
import order.OrderService;
import order.OrderServiceImpl;
import order.PercentDiscountPolicyImpl;
public class AppConfig {
public MemberRepository getMemberRepository() {
return MemoryMemberRepositoryImpl.getInstance(); // 싱글톤 패턴
}
public MemberService getMemberService() {
return new MemberServiceImpl(getMemberRepository()); //DI
}
public DiscountPolicy getDiscountPolicy() {
// 현재 할인 정책 : 고정할인
// 변경 => 퍼센트 할인으로 바꾸면 된다.
return new FixDiscountPolicyImpl(); // 이 부분만 변경하기
// return new PercentDiscountPolicyImpl(); // 이 부분만 변경하기
}
public OrderService getOrderService() {
return new OrderServiceImpl(getMemberRepository(), getDiscountPolicy());
}
}
처음 로직 설계 시 고정 금액으로 설정하였으나 퍼센트 할인으로 변경될 경우
OrderService orderService1 = new OrderServiceImpl(memberRepository, fixDiscountPolicy); 고정 금액 할인 코드를
OrderService orderService1 = new OrderServiceImpl(memberRepository, percentDiscountPolicy); 퍼센트 할인 코드로
변경할 때
Appconfig 클래스의 getDiscountPolicy() 메서드의 return 값을 new PercentDiscountPolicyImple();로 변경해야 한다.
=> 이렇게 설정하면 고정 금액 할인에서 퍼센트 금액 할인으로 쉽게 변경 가능하다.
MainApp
package config;
import member.Grade;
import member.Member;
import member.service.MemberService;
import order.Order;
import order.OrderService;
public class MainApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
// 클라이언트 입장에서 코딩
// 회원 가입 기능이 필요함.
MemberService memberService = appConfig.getMemberService();
Member member = new Member(200L, "도시락", Grade.VIP);
memberService.signUp(member); // 회원가입
System.out.println(memberService.findByIdMember(200L)); // 회원 조회
// 사용자가 주문 요청
OrderService orderService1 = appConfig.getOrderService();
Order order1 = orderService1.createOrder(member.getId(), "아이폰16", 3_000);
System.out.println(order1);
System.out.println(order1.getDiscountPrice());
}
}
고정 할인 금액
OrderServiceTest 클래스에서
OrderService orderService1 = new OrderServiceImpl(memberRepository, fixDiscountPolicy); 고정 금액 할인 코드로,
AppConfig 클래스에서
getDiscountPolicy() 메서드의 return 값을 new FixDiscountPolicyImple();로 설정한다.
실행 결과
상품 금액 3000원에서 고정 금액 1500원을 할인 받아서 할인 후 상품 금액은 1500원임을 확인할 수 있다.
퍼센트 할인 금액
OrderServiceTest 클래스에서
OrderService orderService1 = new OrderServiceImpl(memberRepository, percentDiscountPolicy); 퍼센트 할인 코드로,
AppConfig 클래스에서
getDiscountPolicy() 메서드의 return 값을 new PercentDiscountPolicyImple();로 설정한다.
실행 결과
상품 금액 3000원에서 10% 할인(300원) 받아서 할인 후 상품 금액은 2700원임을 확인할 수 있다.
'Java' 카테고리의 다른 글
Stream API (1) | 2024.10.01 |
---|---|
행위 패턴(전략 패턴) (0) | 2024.09.27 |
구조 패턴(어댑터 패턴) (3) | 2024.09.26 |
생성 패턴(팩토리 패턴) (0) | 2024.09.26 |
생성 패턴(빌더 패턴) (0) | 2024.09.26 |