fkdl0048 / CodeReview

게임 개발자 관련 정보 모음집
5 stars 0 forks source link

도메인 분석/설계 페어 프로그래밍 #36

Closed fkdl0048 closed 3 months ago

fkdl0048 commented 3 months ago

도메인 분석/설계 페어 프로그래밍

오브젝트, 객체지향의 사실과 오해를 읽고 도메인을 설계해보는 시간을 가지고자 세션을 개설

정안 도메인

버스 요금 계산 도메인

기본 요금, 거리 대비 요금, 시간대 요금(야간 할증), 나이대별 할인(어린이, 노인)

버스를 탈 때 NFC태깅이 가능한 카드인지 확인하고, 가능하다면 요금을 조회하여 잔액이 남아 있다면 기본 요금을 결제한다. 만약 잔액이 없다면 잔액부족을 띄운다.

결제된 기본 요금에서 내릴 때, 할증/할인을 계산하여 요금을 부과한다. (내릴 때도 마찬가지로 NFC, 요금조회를 거친다.)

할증/할인 조건은 다음과 같다.

우연 도메인

만화카페 요금제 도메인

당신은 만화카페를 개업한 초보 사장님입니다. 수익을 극대화하고 고객을 유치하기 위해 요금제를 설계해봅시다.

기본 요금 및 추가 요금

구체적인 요금 설정

음료 요금

할인 조건 (중복 가능)

추가 조건 (선택)

도메인 분석 및 설계

기징 먼저 도메인의 흐름을 생각하면 다음과 같다.

손님 - 만화카페 - 요금 - 추가 요금 - 할인 요금

디테일하게 들어가면 다음과 같다.

// 손님 타입 Enum
enum CustomerType
{
    Adult,
    Teenager,
    // 추가 가능성..
}

// 손님
class Customer
{
    private CustomerType customerType;
    private LocalDateTime visitTime;
    private IDrink drink;

    public Customer(CustomerType customerType, LocalDateTime visitTime, IDrink drink)
    {
        this.customerType = customerType;
        this.visitTime = visitTime;
        this.drink = drink;
    }
}

// 음료
interface IDrink
{
    int CalculateFee();
}

class CafeLatte : IDrink
{
  private int additionalFee;

  public CafeLatte(int additionalFee)
  {
      this.additionalFee = additionalFee;
  }

    public int CalculateFee()
    {
        // 카페라떼 추가 요금 계산
    }
}

// 나머지 음료도 같은 방식으로 구현
...
//

// AdditionFee
interface IAdditionFee
{
    int CalculateFee(Customer customer);
}

class WeekdayAdditionFee : IAdditionFee
{
    private Map<CustomerType, int> additionalFee;

    public WeekdayAdditionFee(Map<CustomerType, int> additionalFee)
    {
        this.additionalFee = additionalFee;
    }

    public int CalculateFee(Customer customer)
    {
        // 평일 추가 요금 계산
    }
}

class WeekendAdditionFee : IAdditionFee
{
    private Map<CustomerType, int> additionalFee;

    public WeekendAdditionFee(Map<CustomerType, int> additionalFee)
    {
        this.additionalFee = additionalFee;
    }

    public int CalculateFee(Customer customer)
    {
        // 주말 추가 요금 계산
    }
}

// 나머지도 같은 방식으로 구현

// FeeCalculator
class FeeCalculator
{
    private Map<CustomerType, int> basicFee;
    private Map<CustomerType, IAdditionFee> additionFee;

    public FeeCalculator(Map<CustomerType, int> basicFee, Map<CustomerType, IAdditionFee> additionFee)
    {
        this.basicFee = basicFee;
        this.additionFee = additionFee;
    }

    public int CalculateFee(Customer customer, LocalDateTime leaveTime)
    {
        // 손님 정보를 조회하여 요금 계산
        // 성인, 청소년 구분 후 -> 방문 시간과 날짜를 통해 IAdditionFee에서 조회하여 맞는 정책으로 계산
        // 기본 요금 계산
        // 추가 요금 계산
    }
}

// IDiscount
interface IDiscount
{
    int CalculateDiscount(Customer customer);
}

class MembershipDiscount : IDiscount
{
    public MembershipDiscount(// 여기서 할인 조건에 대한 값을 받아야 하는데 너무 범위가 넓음,)
    {
        // 잘못 설계한 것 같음.. 
        // 다시 한다면 그냥 정적으로 할인 계산을 하는 것이 맞을 것 같다.
    }

    public int CalculateDiscount(Customer customer)
    {
        // 회원가입 유도 할인 계산
    }
}

// DiscountCalculator
class DiscountCalculator
{
    private List<IDiscount> discounts;

    public DiscountCalculator(List<IDiscount> discounts)
    {
        this.discounts = discounts;
    }

    public int CalculateDiscount(Customer customer)
    {
        // 손님 정보를 조회하여 할인 계산
        // 할인 조건에 따라 할인 계산
    }
}

// 만화카페
class ComicCafe
{
    private FeeCalculator feeCalculator;
    private DiscountCalculator discountCalculator;

    public ComicCafe(FeeCalculator feeCalculator, DiscountCalculator discountCalculator)
    {
        this.feeCalculator = feeCalculator;
        this.discountCalculator = discountCalculator;
    }

    public void CalculateFee(Customer customer, LocalDateTime leaveTime, IDrink drink)
    {
        // 요금 계산
        // 추가 요금 계산
        // 할인 계산
    }
}

// Main
public static void main(String[] args)
{
    // 요금 계산기 생성
    Map<CustomerType, int> basicFee = new HashMap<>();
    basicFee.Put(CustomerType.Adult, 9000);
    basicFee.Put(CustomerType.Teenager, 8000);

    Map<CustomerType, IAdditionFee> additionFee = new HashMap<>();
    additionFee.Put(CustomerType.Adult, new WeekdayAdditionFee(// 여기에 map 넣어야 함));
    additionFee.Put(CustomerType.Teenager, new WeekdayAdditionFee(// 여기에 map 넣어야 함));

    FeeCalculator feeCalculator = new FeeCalculator(basicFee, additionFee);

    // 할인 계산기 생성
    List<IDiscount> discounts = new ArrayList<>();
    discounts.Add(new MembershipDiscount());
    discounts.Add(new ReviewDiscount());
    discounts.Add(new SmallFeeDiscount());

    DiscountCalculator discountCalculator = new DiscountCalculator(discounts);

    // 만화카페 생성
    ComicCafe comicCafe = new ComicCafe(feeCalculator, discountCalculator);

    // 손님 생성
    Customer customer = new Customer(CustomerType.Adult, LocalDateTime.now());

    // 요금 계산
    comicCafe.CalculateFee(customer, LocalDateTime.now(), new CafeLatte(500));
}

동희 도메인

쇼핑몰 도메인

목표: 고객이 고른 제품들의 가격을 계산해 주문 가격을 내놓는다.

넘겨지는 정보: 제품들 정보, 주문하는 일시, 사용하는 쿠폰

도메인 분석 및 설계

가장 먼저 흐름에 대한 협력을 설계해보면, 다음과 같다.

고객 - 쇼핑몰 - 제품 - 할인 - 쿠폰

각 역할에 대한 책임을 나누어 본다.

// 고객
class Customer 
{
    private List<Coupon> coupons;
    private Money money;

    public Customer(List<Coupon> coupons, Money money) 
    {
        this.coupons = coupons;
        this.money = money;
    }

    public void Order(ShoppingMall shoppingMall, List<Product> products, LocalDateTime orderTime) 
    {
        shoppingMall.CalculatePrice(products, orderTime, coupons);
        // 금액 계산
    }
}

// 쇼핑몰
class ShoppingMall 
{
    private Map<String, Product> products;

    public ShoppingMall(Map<String, Product> products) 
    {
        this.products = products;
    }

    public void CalculatePrice(List<Product> products, LocalDateTime orderTime, List<Coupon> coupons) 
    {
        // 제품 가격 계산
        // 총 가격 계산
        // 쿠폰 적용
    }
}

// 제품
class Product 
{
    private String name;
    private int price;
    private Discountable discount;

    public Product(String name, int price, Discountable discount) 
    {
        this.name = name;
        this.price = price;
        this.discount = discount;
    }
}

// 할인
interface Discountable 
{
    int CalculateDiscount(int price);
}

class NormalDiscount : Discountable 
{
    public int CalculateDiscount(int price) 
    {
        // 일반 할인 계산
    }
}

class SpecialTimeDiscount : Discountable 
{
    public int CalculateDiscount(int price) 
    {
        // 스페셜 타임 할인 계산
    }
}

// 쿠폰
class Coupon 
{
    private String name;
    private CouponCondition condition;

    public Coupon(String name, CouponCondition condition) 
    {
        this.name = name;
        this.condition = condition;
    }
}

interface CouponCondition 
{
    boolean IsSatisfied();
}

class DayCouponCondition : CouponCondition 
{
    private DayOfWeek dayOfWeek;

    public DayCouponCondition(DayOfWeek dayOfWeek) 
    {
        this.dayOfWeek = dayOfWeek;
    }

    public boolean IsSatisfied() 
    {
        // 요일별 쿠폰 적용
    }
}

class ProductCouponCondition : CouponCondition 
{
    private Product product;

    public ProductCouponCondition(Product product) 
    {
        this.product = product;
    }

    public boolean IsSatisfied() 
    {
        // 제품별 쿠폰 적용
    }
}

// Main
public static void main(String[] args) 
{
    // 제품 정보
    Map<String, Product> products = new HashMap<>();
    products.Put("product1", new Product("product1", 10000, new NormalDiscount()));
    products.Put("product2", new Product("product2", 20000, new SpecialTimeDiscount()));

    // 쇼핑몰 생성
    ShoppingMall shoppingMall = new ShoppingMall(products);

    // 고객 생성
    List<Coupon> coupons = new ArrayList<>();
    coupons.Add(new Coupon("coupon1", new DayCouponCondition(DayOfWeek.MONDAY)));
    coupons.Add(new Coupon("coupon2", new ProductCouponCondition(products.Get("product1"))));
    Customer customer = new Customer(coupons, new Money(100000));

    // 주문
    customer.order(shoppingMall, (products("product1"), products("product2")), LocalDateTime.now());
}