HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
[New] 아만드팀
[New] 아만드팀
/
🔏
팀 스터디
/
📕
객체지향의 사실과 오해
/
👏
07 함께모으기
👏

07 함께모으기

209페이지 커피 예제를 보고 자유롭게 작성, 책 참고 가능

기능 목록

도메인 모델

민환
notion image
수빈
notion image
 
 
우진

역할, 책임, 협력

협력

커피 주문
 

책임

  • 손님
    • 메뉴판에서 커피를 선택할 책임
    • 커피를 주문할 책임
  • 캐시어
    • 손님의 주문을 받을 책임
    • 손님으로부터 받은 주문을 바리스타에게 전달할 책임
    • 커피가 제조되었음을 손님에게 알릴 책임
  • 바리스타
    • 캐시어로부터 주문을 받을 책임
    • 커피를 제조할 책임
    • 커피가 제조되었음을 캐시어에게 알릴 책임
 

역할

  • 손님
  • 캐시어
  • 바리스타
 

도메인 모델

notion image

구현 코드

민환

손님 (Customer.java)

 

메뉴 (Menu.java)

 

메뉴 아이템 (MenuItem.java)

 

바리스타 (Barista.java)

 

커피(Coffee.java)

 

Main

 

 

궁금한 점

  • 책에서는 각 객체를 메서드 파라미터로 넘겨주었다. 필드로 가지고 있는 것과 파라미터로 넘겨주는 것 어떤 것이 더 좋은 설계일까?
    • 개인 의견
      검색과 생각해본 결과 객체의 비즈니스 로직을 많이 사용한다면 필드로 빼는 것이 좋을 것 같다. 즉, 동일한 매개 변수를 여러 메서드에서 사용하는 지 여부를 확인하자!
  • MenuItems라는 일급 컬렉션을 만들려고 했지만 Menu 자체가 일급 컬렉션이다.
  • get과 find 네이밍의 차이에 대해서 알아봅시다.
    • findById ⇒ Optional
    • getRerenceById ⇒ T , throw
 

Menu를 enum으로 사용한다면?

  • map 사용은 한번 해보고 싶었습니다…
    • Enum 조회 성능 높여보기 - HashMap을 이용해서 빠르게 조회해보자
 
  • 장점
    • enum을 사용하면 MenuItem을 생성할 필요가 없으며 확장하고 싶은 경우 상수에 추가만 해주면 된다.
    • (개인 의견) enum은 정적인 경우에만 사용하는 것이 좋을 것 같다.
  • 단점
    • enum이기 때문에 상태가 변화할 수 없다.
      • 아메리카노의 가격을 500원으로 변경해야한다면 변경 후 어플리케이션을 재배포해야함.
    • 상속이나 확장이 일부 제한되기 때문에 유연성이 조금 떨어진다.

그러면 어떻게 해야할까?

  • 처음 예제처럼 MenuItem 도메인으로 구현 후 데이터베이스에서 처리
    • Anti-OOP : if 를 피하고 싶어서
 
수빈

협력 : 커피를 주문해라

Cafe의 책임

Cafe의 역할(책임)을 수행하는 스타벅스

Customer의 책임

Customer의 역할(책임)을 수행하는 Subin 객체

 

Cashier와 Barisata의 책임

 

Cashier와 Barisata의 역할(책임)을 수행하는 Alice와 Rabbit 객체

Menu랑 Coffee

우진

메인

메뉴

고객

캐시어

바리스타

커피

 
 
 

 
 
 
 
 
날짜
Jun 9, 2022
Tags
public class Customer { private final Barista barista; private final Menu menu; public Customer(Barista barista, Menu menu) { this.barista = barista; this.menu = menu; } // 결과값 확인을 위해 반환 public Coffee order(String menuName) { MenuItem menuItem = menu.getMenuByName(menuName); return barista.makeCoffee(menuItem); } }
public class Menu { private final List<MenuItem> menuItems; public Menu(List<MenuItem> menuItems) { this.menuItems = menuItems; } public MenuItem getMenuByName(String menuName) { return menuItems.stream() .filter(menuItem -> menuItem.isSameName(menuName)) .findAny() .orElseThrow(() -> new IllegalArgumentException(String.format("%s에 해당하는 메뉴가 없습니다.", menuName))); } }
public class MenuItem { private final String name; private final int price; public MenuItem(String name, int price) { this.name = name; this.price = price; } public boolean isSameName(String menuName) { return name.equals(menuName); } public String getName() { return name; } public int getPrice() { return price; } }
public class Barista { public Coffee makeCoffee(MenuItem menuItem) { return new Coffee(menuItem); } }
public class Coffee { private final String name; private final int price; public Coffee(String name, int price) { this.name = name; this.price = price; } public Coffee(MenuItem menuItem) { this(menuItem.getName(), menuItem.getPrice()); } // 결과값 확인을 위해 사용 @Override public String toString() { return "Coffee{" + "name='" + name + '\'' + ", price=" + price + '}'; } }
public class CoffeeShop { public static void main(String[] args) { List<MenuItem> menuItems = List.of( new MenuItem("아메리카노", 1500), new MenuItem("카푸치노", 2000), new MenuItem("카라멜 마키야또", 2500), new MenuItem("에스프레소", 2500) ); Menu menu = new Menu(menuItems); Barista barista = new Barista(); Customer customer = new Customer(barista, menu); Coffee coffee = customer.order("아메리카노"); System.out.println(coffee); } }
public enum Menu { AMERICANO("아메리카노", 1500), CAPPUCCINO("카푸치노", 2000), CARAMEL_MACCHIATO("카라멜 마키야또", 2500), ESPRESSO("에스프레소", 2500); private static final Map<String, Menu> MENU_MAP = new HashMap(); static { for (Menu menu : values()) { MENU_MAP.put(menu.getName(), menu); } } private final String name; private final int price; Menu(String name, int price) { this.name = name; this.price = price; } public static Menu getMenuByName(String menuName) { if (!MENU_MAP.containsKey(menuName)) { throw new IllegalArgumentException(String.format("%s에 해당하는 메뉴가 없습니다.", menuName)); } return MENU_MAP.get(menuName); } public String getName() { return name; } public int getPrice() { return price; } }
@Test void 커피주문_협력() { Customer customer = new Subin(); Cafe cafe = new StarBucks(); cafe.provideService(customer); // 카페 : 손님에게 서비스를 제공할 책임 }
interface Cafe { void provideService(Customer customer); }
class StarBucks implements Cafe { Cashier cashier = new Alice(); Barista barista = new Rabbit(); List<Menu> menus = List.of(Menu.AMERICANO, Menu.CAFE_LATTE); public List<Menu> getMenus() { return new ArrayList<>(menus); } @Override public void provideService(Customer customer) { List<Menu> menu = this.getMenus(); // 카페 : 메뉴판 제공할 책임 Menu selectedMenu = customer.selectMenu(menu); // 손님 : 메뉴를 선택할 책임 var coffee = cashier.takeOrder(selectedMenu, barista); // 캐시어 : 선택된 메뉴를 바리스타에게 전달할 책임 customer.drinkCoffee(coffee); // 손님 : 커피를 마실 책임 } }
interface Customer { Menu selectMenu(List<Menu> menus); void drinkCoffee(Coffee coffee); }
class Subin implements Customer { private final Random random = new Random(); @Override public Menu selectMenu(List<Menu> menus) { var index = random.nextInt(menus.size()); return menus.get(index); } @Override public void drinkCoffee(Coffee coffee) { System.out.println(coffee + " 마시는 중..."); } }
interface Cashier { Coffee takeOrder(Menu selected, Barista barista); } interface Barista { Coffee makeCoffee(Menu coffee); }
class Alice implements Cashier { @Override public Coffee takeOrder(Menu selected, Barista barista) { return barista.makeCoffee(selected); } } class Rabbit implements Barista { @Override public Coffee makeCoffee(Menu coffee) { return Coffee.valueOf(coffee.toString()); } }
enum Menu { HAZELNUT, AMERICANO, CAPPUCCINO, CAFE_LATTE; } enum Coffee { HAZELNUT, AMERICANO, CAPPUCCINO, CAFE_LATTE; }
public class Main { public static void main(String[] args) { Customer customer = new Customer(); Coffee coffee = customer.order(new Cashier(), "americano"); } }
public enum Menu { AMERICANO("americano", 3000), CAFE_LATTE("caffeLatte", 4000), ESPRESSO("espresso", 3500), CAPPUCCINO("cappuccino", 3500); private final String menuName; private final int price; Menu(String menuName, int price) { this.menuName = menuName; this.price = price; } public static Menu selectMenu(String menuName) { return Arrays.stream(values()) .filter(menu -> menu.menuName.equals(menuName)) .findAny() .orElseThrow(() -> new IllegalArgumentException("판매하지 않는 메뉴입니다.")); } public String getMenuName() { return menuName; } public int getPrice() { return price; } }
public class Customer { public Coffee order(Cashier cashier, String menuName) { Menu menu = Menu.selectMenu(menuName); return cashier.order(menu, new Barista()); } }
public class Cashier { public Coffee order(Menu menu, Barista barista) { return barista.makeCoffee(menu); } }
public class Barista { public Coffee makeCoffee(Menu menu) { return new Coffee(menu); } }
public class Coffee { private String name; private int price; public Coffee(Menu menu) { this.name = menu.getMenuName(); this.price = menu.getPrice(); } }