LV.4 ~ LV.5 키오스크의 요구사항은 다음과 같다.
Menu
클래스 생성하기List<MenuItem>
을 Menu
클래스가 관리하도록 변경하기MenuItem
, Menu
그리고 Kiosk
클래스의 필드에 직접 접근하지 못하도록 설정하기Getter
와 Setter
메서드를 사용해 데이터를 관리하기먼저 다음과 같이 category
와 menuItems
를 필드로 갖는 Menu
클래스를 구현했다. category
를 넣어 생성하고, getMenuItems()
메서드로 menuItems
을 가져와 MenuItem
클래스를 리스트에 추가할 수 있도록 했다. 또한 카테고리 이름을 반환하는 메서드인 getCategory()
를 구현했다.
public class Menu {
private final String category;
private final List<MenuItem> menuItems = new ArrayList<>();
public Menu(String category) {
this.category = category;
}
public String getCategory() {
return category;
}
public List<MenuItem> getMenuItems() {
return menuItems;
}
}
Main
클래스에 있는 initializeMenu()
에서 다음과 같이 각각의 메뉴 카테고리마다 getMenuItems()
메서드를 이용해 새로운 MenuItem
을 계속 추가할 수 있다.
private static void initializeMenu() {
Menu burgerMenu = new Menu("Burgers");
burgerMenu.getMenuItems().add(new MenuItem("ShackBurger", 6.9, "토마토, 양상추, 쉑소스가 토핑된 치즈버거"));
burgerMenu.getMenuItems().add(new MenuItem("SmokeShack", 8.9, "베이컨, 체리 페퍼에 쉑소스가 토핑된 치즈버거"));
burgerMenu.getMenuItems().add(new MenuItem("Cheeseburger", 6.9, "포테이토 번과 비프패티, 치즈가 토핑된 치즈버거"));
burgerMenu.getMenuItems().add(new MenuItem("Hamburger", 5.4, "비프패티를 기반으로 야채가 들어간 기본버거"));
menus.add(burgerMenu);
}
그리고 Kiosk
클래스도 꽤 많이 고쳤다. start()
메서드가 너무 긴 것 같아서 조금 찜찜하다.. 메인 메뉴 번호와 상세 메뉴 아이템 번호까지 받는 부분을 처리해서 코드가 길어졌다. 주석을 꼼꼼하게 달아놓았기 때문에 알아보는데는 큰 문제가 없겠지만, 좀 더 고민해봐야겠다.
public void start() {
try (Scanner sc = new Scanner(System.in)) {
while (true) {
// 메뉴 출력
showMenu();
System.out.print("메인 메뉴를 선택하세요: ");
if (!sc.hasNextInt()) {
System.out.println("숫자를 입력해주세요.");
sc.next();
continue;
}
// 메뉴 입력 받기
int selectedMenu = sc.nextInt();
if (selectedMenu == 0) {
System.out.println("프로그램을 종료합니다.");
break;
}
if (isValidMenu(selectedMenu)) {
// 메뉴 아이템 출력
showMenuItems(selectedMenu);
// 메뉴 아이템 입력 받기
System.out.print("메뉴를 선택하세요: ");
int selectedMenuItem = sc.nextInt();
// 0 입력 시 뒤로가기(메인 메뉴)
if (selectedMenuItem == 0) continue;
// 선택한 메뉴 아이템 출력
if (isValidMenuItem(selectedMenu, selectedMenuItem)) {
displaySelectedMenuItems(selectedMenu, selectedMenuItem);
} else {
System.out.println("올바른 메뉴 아이템 번호를 입력해주세요.");
}
} else { // 메뉴에 없는 값을 입력할 경우
System.out.println("올바른 메뉴 번호를 입력해주세요.");
}
System.out.println();
}
}
}
메뉴 선택 시 IndexOutOfBoundsException
이 발생했다. 그나마 고치기 쉬운 에러라 다행이다.🙄
menus.get(input)
을 할 때 input
을 그대로 넣은 것이 문제였다. 인덱스는 0
부터 시작인데 길이가 3개인 리스트에서 3번째 데이터를 가져오려고 하면 오류가 나는 것은 당연하다. menus.get(input - 1)
로 수정해주었다.
private void showMenuItems(int input) {
System.out.printf("[ %s MENU ]", menus.get(input).getCategory()); // 에러 발생
System.out.println();
int size = menus.get(input).getMenuItems().size(); // 에러 발생
for (int i = 0; i < size; i++) {
MenuItem menuItem = menus.get(input).getMenuItems().get(i); // 에러 발생
System.out.printf("%d. %s | W %.1f | %s%n", i + 1,
menuItem.getName(), menuItem.getPrice(), menuItem.getDescription());
}
System.out.println("0. 뒤로가기");
}
"의존한다" 라는 것은 뭘까? 다음과 같이 MyList를 구현하는 MyArrayList와 MyArrayList를 만들었다.
public interface MyList<E> {
int size();
void add(E e);
void add(int index, E e);
E get(int index);
E set(int index, E element);
E remove(int index);
int indexOf(E o);
}
public class MyArrayList<E> implements MyList<E> {
//...
}
public class MyLinkedList<E> implements MyList<E> {
//...
}
MyArrayList 를 활용해서 많은 데이터를 처리하는 BatchProcessor 클래스를 개발하고 있다고 할 때, 데이터를 앞에서 추가하는 일이 많은 상황이 올 수 있다. 그러면 그때마다 MyLinkedList 를 사용하도록 코드를 변경해야 한다. 이런 상황을 BatchProcessor가 구체적인 클래스에 의존한다고 표현한다.
public class BatchProcessor {
private final MyArrayList<Integer> list = new MyArrayList<>();
public BatchProcessor(MyList<Integer> list) {
this.list = list;
}
public void logic(int size) {
long startTime = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
list.add(0, i); // 앞에 추가
}
long endTime = System.currentTimeMillis();
System.out.println("크기: " + size +
", 계산 시간: " + (endTime - startTime) + "ms");
}
}
구체적인 클래스에 의존하는 대신, 추상적인 MyList 인터페이스에 의존할 수 있다. 이렇게 하면 BatchProcessor를 생성하는 시점에 리스트 전략을 선택해 전달하면 되고, 클라이언트 코드인 BatchProcessor의 변경 없이도 MyArrayList에서 MyLinkedList로 변경하는 것이 가능하다. 바로 이것을 의존 관계 주입(Dependency Injection, DI)이라고 한다.
public class BatchProcessor {
private final MyList<Integer> list = new MyList<>();
public BatchProcessor(MyList<Integer> list) {
this.list = list;
}
public void logic(int size) {
long startTime = System.currentTimeMillis();
for (int i = 0; i < size; i++) {
list.add(0, i); // 앞에 추가
}
long endTime = System.currentTimeMillis();
System.out.println("크기: " + size +
", 계산 시간: " + (endTime - startTime) + "ms");
}
}
오늘은 계획한 일을 거의 끝냈지만, 뭔가 완전히 집중하진 못했던 것 같다.