필수 기능 구현 마지막 요구사항이다.
MenuItem 을 관리하는 Menu 클래스를 생성하라는 문제이다.
0을 입력했을 때 하위 카테고리는 "뒤로가기" 상위 카테고리에선 "종료" 로 구분하는 부분이 추가되었다.
현재 콘솔 기반 프로그램이고 DB가 존재하지 않아 관리-프로그램까진 고려하지 않았고 그냥 메뉴 데이터를 하드코딩으로 박아 넣는 느낌이다.
그래도 Lv3 에서 for 문을 통해 메뉴 리스트를 출력하는걸 좀 더 보완해보고자 한다.
Lv 4. 객체 지향 설계를 적용해 음식 메뉴와 주문 내역을 클래스 기반으로 관리하기
- [ ] Menu 클래스 생성하기
- [ ] 설명 : MenuItem 클래스를 관리하는 클래스입니다. 예를 들어, 버거 메뉴, 음료 메뉴 등 각 카테고리 내에 여러 MenuItem을 포함합니다.
- [ ] List<MenuItem> 은 Kiosk 클래스가 관리하기에 적절하지 않으므로 Menu 클래스가 관리하도록 변경합니다.
- [ ] 여러 버거들을 포함하는 상위 개념 ‘버거’ 같은 카테고리 이름 필드를 갖습니다.
- [ ] 메뉴 카테고리 이름을 반환하는 메서드가 구현되어야 합니다.
[ MAIN MENU ]
1. Burgers
2. Drinks
3. Desserts
0. 종료 | 종료
1 <- // 1을 입력
[ BURGERS MENU ]
1. ShackBurger | W 6.9 | 토마토, 양상추, 쉑소스가 토핑된 치즈버거
2. SmokeShack | W 8.9 | 베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거
3. Cheeseburger | W 6.9 | 포테이토 번과 비프패티, 치즈가 토핑된 치즈버거
4. Hamburger | W 5.4 | 비프패티를 기반으로 야채가 들어간 기본버거
0. 뒤로가기
2 <- // 2를 입력
선택한 메뉴: SmokeShack | W 8.9 | 베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거
[ MAIN MENU ]
1. Burgers
2. Drinks
3. Desserts
0. 종료 | 종료
0 <- // 0을 입력
프로그램을 종료합니다.
Lv 5. 캡슐화 적용하기
- [ ] MenuItem, Menu 그리고 Kiosk 클래스의 필드에 직접 접근하지 못하도록 설정합니다.
- [ ] Getter와 Setter 메서드를 사용해 데이터를 관리합니다.
com.example.lv4
├── domain // 핵심 비즈니스 도메인 객체
├── Menu.java
└── MenuItem.java
├── enums // Enum 타입들
├── Category.java
└── KioskStatus.java
├── input // 입력 관련 인터페이스 및 구현체
├── InputProvider.java
└── ConsoleInputProvider.java
├── output // 출력 관련 인터페이스 및 구현체
├── OutputWriter.java
└── ConsoleOutputWriter.java
├── repository // 데이터 저장소 관련
├── MenuRepository.java // 인터페이스
└── MemoryMenuRepository.java // 메모리 구현체
├── service // 비즈니스 로직 처리 서비스 계층
└── MenuService.java
├── utils // 유틸리티 클래스 (파싱, 공통 함수 등)
└── Parser.java
├── Kiosk.java
└── Main.java
뭐가 많이 생기긴 했지만 데이터 저장 과정을 위해 repository와 service를 추가했다.
또 다른 이유로는 요구 사항에선 Menu 에서 List<MenuItem> 을 관리하도록 제안했지만 Menu 라는 도메인 자체가 Kiosk 에 주입되어 콘솔 로직과 같이 동작하는게 맘에 안들어서 추가했다.
MemoryMenuRepository.java
package com.example.lv4.repository;
public class MemoryMenuRepository implements MenuRepository {
private static final Map<Long, Menu> store = new HashMap<>();
@Override
public void save(Menu menu) {
store.put(menu.getId(), menu);
}
@Override
public List<Menu> findAll() {
return store.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.map(Map.Entry::getValue)
.toList();
}
@Override
public List<MenuItem> findByCategory(Category category) {
Map<Category, List<MenuItem>> sortedMenuItemByCategory = store.values().stream()
.flatMap(menu -> menu.getMenuItems().stream())
.sorted(Comparator.comparing(MenuItem::getId))
.collect(Collectors.groupingBy(
MenuItem::getCategory,
LinkedHashMap::new,
Collectors.toList()
));
return sortedMenuItemByCategory.getOrDefault(category, Collections.emptyList());
}
@Override
public Menu findById(Long id) {
return store.get(id);
}
}
MenuService.java
package com.example.lv4.service;
public class MenuService {
private final MenuRepository menuRepository;
public MenuService(MenuRepository menuRepository) {
this.menuRepository = menuRepository;
}
public void save(Menu menu) {
menuRepository.save(menu);
}
public Menu findById(Long id) {
return menuRepository.findById(id);
}
public List<Menu> findAllMenu() {
return menuRepository.findAll();
}
public List<MenuItem> findAllMenuItemByCategory(Category category) {
return menuRepository.findByCategory(category);
}
}
덩치가 커지긴 했지만 적절하게 "단일 책임 원칙" 에 입각하여 구조가 구성된거 같다.
또한 각각의 역할을 명확하게 구분했기 때문에 추후 변경 사항에도 유연하게 대처가 가능할 거 같다.