티스토리 뷰

Java

OOP 회원과 주문 관리 설계

yoooon1212 2024. 10. 1. 17:40

회원가입 시스템 설계(도메인 - 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
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
글 보관함