From 6ef0c5222ca7e2639c8ea17ed8494a8323d3b5af Mon Sep 17 00:00:00 2001 From: unifolio0 Date: Sun, 21 Dec 2025 15:57:09 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat:=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +- gradle/wrapper/gradle-wrapper.properties | 2 +- src/main/java/menu/Application.java | 8 +- src/main/java/menu/MenuRecommendation.java | 78 +++++++++++++++++++ src/main/java/menu/domain/FoodCategory.java | 33 ++++++++ .../java/menu/domain/ImpossibilityMenu.java | 34 ++++++++ src/main/java/menu/domain/MenuBoard.java | 38 +++++++++ src/main/java/menu/domain/Person.java | 29 +++++++ src/main/java/menu/domain/Persons.java | 33 ++++++++ src/main/java/menu/exception/ErrorCode.java | 36 +++++++++ src/main/java/menu/view/InputView.java | 27 +++++++ src/main/java/menu/view/OutputView.java | 33 ++++++++ 12 files changed, 351 insertions(+), 5 deletions(-) create mode 100644 src/main/java/menu/MenuRecommendation.java create mode 100644 src/main/java/menu/domain/FoodCategory.java create mode 100644 src/main/java/menu/domain/ImpossibilityMenu.java create mode 100644 src/main/java/menu/domain/MenuBoard.java create mode 100644 src/main/java/menu/domain/Person.java create mode 100644 src/main/java/menu/domain/Persons.java create mode 100644 src/main/java/menu/exception/ErrorCode.java create mode 100644 src/main/java/menu/view/InputView.java create mode 100644 src/main/java/menu/view/OutputView.java diff --git a/build.gradle b/build.gradle index cf1e54f32..f12f02cdc 100644 --- a/build.gradle +++ b/build.gradle @@ -15,9 +15,8 @@ dependencies { } java { - toolchain { - languageVersion = JavaLanguageVersion.of(11) - } + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } test { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 28ff446a2..a59520664 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/menu/Application.java b/src/main/java/menu/Application.java index 6340b6f33..95188617d 100644 --- a/src/main/java/menu/Application.java +++ b/src/main/java/menu/Application.java @@ -1,7 +1,13 @@ package menu; +import menu.view.InputView; +import menu.view.OutputView; + public class Application { public static void main(String[] args) { - // TODO: 프로그램 구현 + InputView inputView = new InputView(); + OutputView outputView = new OutputView(); + MenuRecommendation menuRecommendation = new MenuRecommendation(inputView, outputView); + menuRecommendation.run(); } } diff --git a/src/main/java/menu/MenuRecommendation.java b/src/main/java/menu/MenuRecommendation.java new file mode 100644 index 000000000..8b9fe0de7 --- /dev/null +++ b/src/main/java/menu/MenuRecommendation.java @@ -0,0 +1,78 @@ +package menu; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import menu.domain.FoodCategory; +import menu.domain.ImpossibilityMenu; +import menu.domain.MenuBoard; +import menu.domain.Person; +import menu.domain.Persons; +import menu.view.InputView; +import menu.view.OutputView; + +public class MenuRecommendation { + + private final InputView inputView; + private final OutputView outputView; + + public MenuRecommendation(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public void run() { + outputView.printStartApplication(); + Persons persons = retryOnError(this::getPersons); + List impossibilityMenus = new ArrayList<>(); + for (Person person : persons.getPersons()) { + ImpossibilityMenu impossibilityMenu = retryOnError(() -> { + List menus = inputView.readImpossibilityMenuName(person.getName()); + return new ImpossibilityMenu(person, menus); + }); + impossibilityMenus.add(impossibilityMenu); + } + List categories = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + categories.add(FoodCategory.from(Randoms.pickNumberInRange(1, 5))); + } + Map> recommendMenus = new LinkedHashMap<>(); + for (Person person : persons.getPersons()) { + recommendMenus.put(person, new ArrayList<>()); + } + for (FoodCategory category : categories) { + for (Person person : persons.getPersons()) { + while (true) { + String menu = MenuBoard.getMenusExcluding(category, impossibilityMenus.stream() + .filter(im -> im.getPerson().equals(person)) + .findFirst() + .orElseThrow() + .getImpossibilityMenus()); + if (!recommendMenus.get(person).contains(menu)) { + recommendMenus.get(person).add(menu); + break; + } + } + } + } + outputView.printRecommendationResult(categories, recommendMenus); + } + + private Persons getPersons() { + List personNames = inputView.readPersonNames(); + return new Persons(personNames); + } + + private T retryOnError(Supplier supplier) { + while (true) { + try { + return supplier.get(); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } +} diff --git a/src/main/java/menu/domain/FoodCategory.java b/src/main/java/menu/domain/FoodCategory.java new file mode 100644 index 000000000..c641d39a8 --- /dev/null +++ b/src/main/java/menu/domain/FoodCategory.java @@ -0,0 +1,33 @@ +package menu.domain; + +import java.util.Arrays; +import menu.exception.ErrorCode; + +public enum FoodCategory { + + JAPANESE(1, "일식"), + KOREAN(2, "한식"), + CHINESE(3, "중식"), + ASIAN(4, "아시안"), + WESTERN(5, "양식"), + ; + + private final int number; + private final String name; + + FoodCategory(int number, String name) { + this.number = number; + this.name = name; + } + + public static FoodCategory from(int number) { + return Arrays.stream(values()) + .filter(category -> category.number == number) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException(ErrorCode.INVALID_NUMBER.getMessage())); + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/menu/domain/ImpossibilityMenu.java b/src/main/java/menu/domain/ImpossibilityMenu.java new file mode 100644 index 000000000..ac31461ac --- /dev/null +++ b/src/main/java/menu/domain/ImpossibilityMenu.java @@ -0,0 +1,34 @@ +package menu.domain; + +import java.util.List; +import menu.exception.ErrorCode; + +public class ImpossibilityMenu { + + public static final int MAX_IMPOSSIBILITY_MENUS_COUNT = 2; + + private final Person person; + private final List impossibilityMenus; + + public ImpossibilityMenu(Person person, List impossibilityMenus) { + validate(impossibilityMenus); + impossibilityMenus + .forEach(MenuBoard::validateMenuName); + this.person = person; + this.impossibilityMenus = impossibilityMenus; + } + + private void validate(List impossibilityMenus) { + if (impossibilityMenus.size() > MAX_IMPOSSIBILITY_MENUS_COUNT) { + throw new IllegalArgumentException(ErrorCode.INVALID_MAX_IMPOSSIBILITY_MENUS_COUNT.getMessage()); + } + } + + public Person getPerson() { + return person; + } + + public List getImpossibilityMenus() { + return impossibilityMenus; + } +} diff --git a/src/main/java/menu/domain/MenuBoard.java b/src/main/java/menu/domain/MenuBoard.java new file mode 100644 index 000000000..55fa65357 --- /dev/null +++ b/src/main/java/menu/domain/MenuBoard.java @@ -0,0 +1,38 @@ +package menu.domain; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.List; +import java.util.Map; +import menu.exception.ErrorCode; + +public class MenuBoard { + + private static final Map> menuBoard = Map.of( + FoodCategory.JAPANESE, List.of("규동", "우동", "미소시루", "스시", "가츠동", "오니기리", "하이라이스", "라멘", "오코노미야끼"), + FoodCategory.KOREAN, List.of("김밥", "김치찌개", "쌈밥", "된장찌개", "비빔밥", "칼국수", "불고기", "떡볶이", "제육볶음"), + FoodCategory.CHINESE, List.of("깐풍기", "볶음면", "동파육", "짜장면", "짬뽕", "마파두부", "탕수육", "토마토 달걀볶음", "고추잡채"), + FoodCategory.ASIAN, List.of("팟타이", "카오 팟", "나시고렝", "파인애플 볶음밥", "쌀국수", "똠얌꿍", "반미", "월남쌈", "분짜"), + FoodCategory.WESTERN, List.of("라자냐", "그라탱", "뇨끼", "끼슈", "프렌치 토스트", "바게트", "스파게티", "피자", "파니니") + ); + + private MenuBoard() {} + + public static void validateMenuName(String menuName) { + for (List menus : menuBoard.values()) { + if (menus.contains(menuName)) { + return; + } + } + throw new IllegalArgumentException(ErrorCode.INVALID_MENU_NAME.getMessage()); + } + + public static String getMenusExcluding(FoodCategory category, List impossibilityMenus) { + List menus = menuBoard.get(category); + while (true) { + String menu = Randoms.shuffle(menus).get(0); + if (!impossibilityMenus.contains(menu)) { + return menu; + } + } + } +} diff --git a/src/main/java/menu/domain/Person.java b/src/main/java/menu/domain/Person.java new file mode 100644 index 000000000..9967fe497 --- /dev/null +++ b/src/main/java/menu/domain/Person.java @@ -0,0 +1,29 @@ +package menu.domain; + +import menu.exception.ErrorCode; + +public class Person { + + public static final int MIN_NAME_LENGTH = 2; + public static final int MAX_NAME_LENGTH = 4; + + private final String name; + + public Person(String name) { + validate(name); + this.name = name; + } + + private void validate(String name) { + if (name == null || name.isBlank()) { + throw new IllegalArgumentException(ErrorCode.INVALID_NAME.getMessage()); + } + if (name.length() < MIN_NAME_LENGTH || name.length() > MAX_NAME_LENGTH) { + throw new IllegalArgumentException(ErrorCode.INVALID_NAME_LENGTH.getMessage()); + } + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/menu/domain/Persons.java b/src/main/java/menu/domain/Persons.java new file mode 100644 index 000000000..3295cc29f --- /dev/null +++ b/src/main/java/menu/domain/Persons.java @@ -0,0 +1,33 @@ +package menu.domain; + +import java.util.List; +import java.util.stream.Collectors; +import menu.exception.ErrorCode; + +public class Persons { + + public static final int MIN_PERSONS_COUNT = 2; + public static final int MAX_PERSONS_COUNT = 5; + + private final List persons; + + public Persons(List names) { + validate(names); + this.persons = names.stream() + .map(Person::new) + .collect(Collectors.toUnmodifiableList()); + } + + private void validate(List names) { + if (names.size() < MIN_PERSONS_COUNT) { + throw new IllegalArgumentException(ErrorCode.INVALID_MIN_PERSONS_COUNT.getMessage()); + } + if (names.size() > MAX_PERSONS_COUNT) { + throw new IllegalArgumentException(ErrorCode.INVALID_MAX_PERSONS_COUNT.getMessage()); + } + } + + public List getPersons() { + return persons; + } +} diff --git a/src/main/java/menu/exception/ErrorCode.java b/src/main/java/menu/exception/ErrorCode.java new file mode 100644 index 000000000..601850ded --- /dev/null +++ b/src/main/java/menu/exception/ErrorCode.java @@ -0,0 +1,36 @@ +package menu.exception; + +import menu.domain.ImpossibilityMenu; +import menu.domain.Person; +import menu.domain.Persons; + +public enum ErrorCode { + + INVALID_NAME("이름은 빈 칸일 수 없습니다"), + INVALID_NAME_LENGTH( + String.format("이름은 %d자 이상 %d자 이하이어야 합니다", Person.MIN_NAME_LENGTH, Person.MAX_NAME_LENGTH) + ), + INVALID_MIN_PERSONS_COUNT( + String.format("코치는 최소 %d명 이상 입력해야 합니다.", Persons.MIN_PERSONS_COUNT) + ), + INVALID_MAX_PERSONS_COUNT( + String.format("코치는 최대 %d명 이하로 입력해야 합니다.", Persons.MAX_PERSONS_COUNT) + ), + INVALID_MENU_NAME("존재하지 않는 메뉴 이름입니다."), + INVALID_MAX_IMPOSSIBILITY_MENUS_COUNT( + String.format("불가능한 메뉴는 최대 2개까지 지정할 수 있습니다.", ImpossibilityMenu.MAX_IMPOSSIBILITY_MENUS_COUNT) + ), + INVALID_NUMBER("해당 번호의 카테고리는 없습니다."); + + private static final String prefix = "[ERROR] "; + + private final String message; + + ErrorCode(String message) { + this.message = message; + } + + public String getMessage() { + return prefix + message; + } +} diff --git a/src/main/java/menu/view/InputView.java b/src/main/java/menu/view/InputView.java new file mode 100644 index 000000000..59df1c325 --- /dev/null +++ b/src/main/java/menu/view/InputView.java @@ -0,0 +1,27 @@ +package menu.view; + +import camp.nextstep.edu.missionutils.Console; +import java.util.List; +import java.util.Scanner; + +public class InputView { + + private final Scanner scanner = new Scanner(System.in); + + public List readPersonNames() { + System.out.println("코치의 이름을 입력해 주세요. (, 로 구분)"); + List names = List.of(scanner.nextLine() + .split(",")); + System.out.println(); + return names; + } + + public List readImpossibilityMenuName(String name) { + String format = String.format("%s(이)가 못 먹는 메뉴를 입력해 주세요.", name); + System.out.println(format); + List menus = List.of(scanner.nextLine() + .split(",")); + System.out.println(); + return menus; + } +} diff --git a/src/main/java/menu/view/OutputView.java b/src/main/java/menu/view/OutputView.java new file mode 100644 index 000000000..4bcc03564 --- /dev/null +++ b/src/main/java/menu/view/OutputView.java @@ -0,0 +1,33 @@ +package menu.view; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import menu.domain.FoodCategory; +import menu.domain.Person; + +public class OutputView { + + public void printStartApplication() { + System.out.println("점심 메뉴 추천을 시작합니다."); + System.out.println(); + } + + public void printRecommendationResult(List categories, Map> menus) { + System.out.println("메뉴 추천 결과입니다."); + System.out.println("[ 구분 | 월요일 | 화요일 | 수요일 | 목요일 | 금요일 ]"); + String categoryLine = String.join(" | ", + categories.stream().map(FoodCategory::getName).collect(Collectors.toList())); + System.out.println("[ 카테고리 | " + categoryLine + " ]"); + for (Person person : menus.keySet()) { + String menuLine = String.join(" | ", menus.get(person)); + System.out.println("[ " + person.getName() + " | " + menuLine + " ]"); + } + System.out.println(); + System.out.println("추천을 완료했습니다."); + } + + public void printErrorMessage(String message) { + System.out.println(message); + } +} From 47a57b9f8de451605132f6470de0e2785284fc1d Mon Sep 17 00:00:00 2001 From: coins117 Date: Sun, 21 Dec 2025 16:06:57 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20=EA=B5=AC=ED=98=84=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/menu/MenuRecommendation.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/menu/MenuRecommendation.java b/src/main/java/menu/MenuRecommendation.java index 8b9fe0de7..16902a6d3 100644 --- a/src/main/java/menu/MenuRecommendation.java +++ b/src/main/java/menu/MenuRecommendation.java @@ -36,8 +36,17 @@ public void run() { impossibilityMenus.add(impossibilityMenu); } List categories = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - categories.add(FoodCategory.from(Randoms.pickNumberInRange(1, 5))); + while (categories.size() < 5) { + FoodCategory category = FoodCategory.from(Randoms.pickNumberInRange(1, 5)); + int count = 0; + for (FoodCategory existingCategory : categories) { + if (existingCategory == category) { + count++; + } + } + if (count < 2) { + categories.add(category); + } } Map> recommendMenus = new LinkedHashMap<>(); for (Person person : persons.getPersons()) { From 135f6ff2a82448a1ca153c5b80ede9267a114c0a Mon Sep 17 00:00:00 2001 From: coins117 Date: Sun, 21 Dec 2025 16:27:44 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/menu/MenuRecommendation.java | 52 ++++++++++--------- .../java/menu/domain/ImpossibilityMenu.java | 11 +--- .../java/menu/domain/ImpossibilityMenus.java | 22 ++++++++ src/main/java/menu/view/InputView.java | 1 - 4 files changed, 51 insertions(+), 35 deletions(-) create mode 100644 src/main/java/menu/domain/ImpossibilityMenus.java diff --git a/src/main/java/menu/MenuRecommendation.java b/src/main/java/menu/MenuRecommendation.java index 16902a6d3..f8efe6c0a 100644 --- a/src/main/java/menu/MenuRecommendation.java +++ b/src/main/java/menu/MenuRecommendation.java @@ -8,6 +8,7 @@ import java.util.function.Supplier; import menu.domain.FoodCategory; import menu.domain.ImpossibilityMenu; +import menu.domain.ImpossibilityMenus; import menu.domain.MenuBoard; import menu.domain.Person; import menu.domain.Persons; @@ -27,24 +28,14 @@ public MenuRecommendation(InputView inputView, OutputView outputView) { public void run() { outputView.printStartApplication(); Persons persons = retryOnError(this::getPersons); - List impossibilityMenus = new ArrayList<>(); - for (Person person : persons.getPersons()) { - ImpossibilityMenu impossibilityMenu = retryOnError(() -> { - List menus = inputView.readImpossibilityMenuName(person.getName()); - return new ImpossibilityMenu(person, menus); - }); - impossibilityMenus.add(impossibilityMenu); - } + ImpossibilityMenus impossibilityMenus = getImpossibilityMenus(persons); List categories = new ArrayList<>(); while (categories.size() < 5) { FoodCategory category = FoodCategory.from(Randoms.pickNumberInRange(1, 5)); - int count = 0; - for (FoodCategory existingCategory : categories) { - if (existingCategory == category) { - count++; - } - } - if (count < 2) { + List foodCategories = categories.stream() + .filter(existingCategory -> existingCategory == category) + .toList(); + if (foodCategories.size() < 2) { categories.add(category); } } @@ -55,11 +46,8 @@ public void run() { for (FoodCategory category : categories) { for (Person person : persons.getPersons()) { while (true) { - String menu = MenuBoard.getMenusExcluding(category, impossibilityMenus.stream() - .filter(im -> im.getPerson().equals(person)) - .findFirst() - .orElseThrow() - .getImpossibilityMenus()); + List impossibilityMenu = impossibilityMenus.getImpossibilityMenu(person); + String menu = MenuBoard.getMenusExcluding(category, impossibilityMenu); if (!recommendMenus.get(person).contains(menu)) { recommendMenus.get(person).add(menu); break; @@ -70,11 +58,6 @@ public void run() { outputView.printRecommendationResult(categories, recommendMenus); } - private Persons getPersons() { - List personNames = inputView.readPersonNames(); - return new Persons(personNames); - } - private T retryOnError(Supplier supplier) { while (true) { try { @@ -84,4 +67,23 @@ private T retryOnError(Supplier supplier) { } } } + + private Persons getPersons() { + List personNames = inputView.readPersonNames(); + return new Persons(personNames); + } + + private ImpossibilityMenus getImpossibilityMenus(Persons persons) { + ImpossibilityMenus impossibilityMenus = new ImpossibilityMenus(); + for (Person person : persons.getPersons()) { + ImpossibilityMenu menu = retryOnError(() -> getImpossibilityMenu(person)); + impossibilityMenus.addImpossibilityMenu(person, menu); + } + return impossibilityMenus; + } + + private ImpossibilityMenu getImpossibilityMenu(Person person) { + List menus = inputView.readImpossibilityMenuName(person.getName()); + return new ImpossibilityMenu(menus); + } } diff --git a/src/main/java/menu/domain/ImpossibilityMenu.java b/src/main/java/menu/domain/ImpossibilityMenu.java index ac31461ac..eaa83b5aa 100644 --- a/src/main/java/menu/domain/ImpossibilityMenu.java +++ b/src/main/java/menu/domain/ImpossibilityMenu.java @@ -7,14 +7,11 @@ public class ImpossibilityMenu { public static final int MAX_IMPOSSIBILITY_MENUS_COUNT = 2; - private final Person person; private final List impossibilityMenus; - public ImpossibilityMenu(Person person, List impossibilityMenus) { + public ImpossibilityMenu(List impossibilityMenus) { validate(impossibilityMenus); - impossibilityMenus - .forEach(MenuBoard::validateMenuName); - this.person = person; + impossibilityMenus.forEach(MenuBoard::validateMenuName); this.impossibilityMenus = impossibilityMenus; } @@ -24,10 +21,6 @@ private void validate(List impossibilityMenus) { } } - public Person getPerson() { - return person; - } - public List getImpossibilityMenus() { return impossibilityMenus; } diff --git a/src/main/java/menu/domain/ImpossibilityMenus.java b/src/main/java/menu/domain/ImpossibilityMenus.java new file mode 100644 index 000000000..ccd9d362d --- /dev/null +++ b/src/main/java/menu/domain/ImpossibilityMenus.java @@ -0,0 +1,22 @@ +package menu.domain; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class ImpossibilityMenus { + + private final Map impossibilityMenus; + + public ImpossibilityMenus() { + this.impossibilityMenus = new LinkedHashMap<>(); + } + + public void addImpossibilityMenu(Person person, ImpossibilityMenu menus) { + impossibilityMenus.put(person, menus); + } + + public List getImpossibilityMenu(Person person) { + return impossibilityMenus.get(person).getImpossibilityMenus(); + } +} diff --git a/src/main/java/menu/view/InputView.java b/src/main/java/menu/view/InputView.java index 59df1c325..1bef08c30 100644 --- a/src/main/java/menu/view/InputView.java +++ b/src/main/java/menu/view/InputView.java @@ -1,6 +1,5 @@ package menu.view; -import camp.nextstep.edu.missionutils.Console; import java.util.List; import java.util.Scanner; From 8a0eb2bb57ebf4f5b17e0f6da49f0b9cd5cdc9a2 Mon Sep 17 00:00:00 2001 From: coins117 Date: Sun, 21 Dec 2025 16:47:09 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/menu/MenuRecommendation.java | 15 +++------ src/main/java/menu/domain/FoodCategory.java | 16 ++++++++++ .../java/menu/domain/ImpossibilityMenus.java | 4 +-- src/main/java/menu/domain/MenuBoard.java | 13 ++++---- .../domain/RecommendationFoodCategories.java | 32 +++++++++++++++++++ 5 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 src/main/java/menu/domain/RecommendationFoodCategories.java diff --git a/src/main/java/menu/MenuRecommendation.java b/src/main/java/menu/MenuRecommendation.java index f8efe6c0a..371051a35 100644 --- a/src/main/java/menu/MenuRecommendation.java +++ b/src/main/java/menu/MenuRecommendation.java @@ -1,6 +1,5 @@ package menu; -import camp.nextstep.edu.missionutils.Randoms; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; @@ -12,6 +11,7 @@ import menu.domain.MenuBoard; import menu.domain.Person; import menu.domain.Persons; +import menu.domain.RecommendationFoodCategories; import menu.view.InputView; import menu.view.OutputView; @@ -29,16 +29,9 @@ public void run() { outputView.printStartApplication(); Persons persons = retryOnError(this::getPersons); ImpossibilityMenus impossibilityMenus = getImpossibilityMenus(persons); - List categories = new ArrayList<>(); - while (categories.size() < 5) { - FoodCategory category = FoodCategory.from(Randoms.pickNumberInRange(1, 5)); - List foodCategories = categories.stream() - .filter(existingCategory -> existingCategory == category) - .toList(); - if (foodCategories.size() < 2) { - categories.add(category); - } - } + + RecommendationFoodCategories recommendationFoodCategories = new RecommendationFoodCategories(); + List categories = recommendationFoodCategories.getRecommendationFoodCategories(); Map> recommendMenus = new LinkedHashMap<>(); for (Person person : persons.getPersons()) { recommendMenus.put(person, new ArrayList<>()); diff --git a/src/main/java/menu/domain/FoodCategory.java b/src/main/java/menu/domain/FoodCategory.java index c641d39a8..7d35e90d4 100644 --- a/src/main/java/menu/domain/FoodCategory.java +++ b/src/main/java/menu/domain/FoodCategory.java @@ -27,6 +27,22 @@ public static FoodCategory from(int number) { .orElseThrow(() -> new IllegalArgumentException(ErrorCode.INVALID_NUMBER.getMessage())); } + public static int getFirstNumber() { + return Arrays.stream(values()) + .map(category -> category.number) + .sorted() + .toList() + .get(0); + } + + public static int getLastNumber() { + return Arrays.stream(values()) + .map(category -> category.number) + .sorted((o1, o2) -> o2 - o1) + .toList() + .get(0); + } + public String getName() { return name; } diff --git a/src/main/java/menu/domain/ImpossibilityMenus.java b/src/main/java/menu/domain/ImpossibilityMenus.java index ccd9d362d..70b50ca76 100644 --- a/src/main/java/menu/domain/ImpossibilityMenus.java +++ b/src/main/java/menu/domain/ImpossibilityMenus.java @@ -1,6 +1,6 @@ package menu.domain; -import java.util.LinkedHashMap; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -9,7 +9,7 @@ public class ImpossibilityMenus { private final Map impossibilityMenus; public ImpossibilityMenus() { - this.impossibilityMenus = new LinkedHashMap<>(); + this.impossibilityMenus = new HashMap<>(); } public void addImpossibilityMenu(Person person, ImpossibilityMenu menus) { diff --git a/src/main/java/menu/domain/MenuBoard.java b/src/main/java/menu/domain/MenuBoard.java index 55fa65357..83d61ad4f 100644 --- a/src/main/java/menu/domain/MenuBoard.java +++ b/src/main/java/menu/domain/MenuBoard.java @@ -15,15 +15,16 @@ public class MenuBoard { FoodCategory.WESTERN, List.of("라자냐", "그라탱", "뇨끼", "끼슈", "프렌치 토스트", "바게트", "스파게티", "피자", "파니니") ); - private MenuBoard() {} + private MenuBoard() { + } public static void validateMenuName(String menuName) { - for (List menus : menuBoard.values()) { - if (menus.contains(menuName)) { - return; - } + List isContains = menuBoard.values().stream() + .map(menus -> menus.contains(menuName)) + .toList(); + if (!isContains.contains(true)) { + throw new IllegalArgumentException(ErrorCode.INVALID_MENU_NAME.getMessage()); } - throw new IllegalArgumentException(ErrorCode.INVALID_MENU_NAME.getMessage()); } public static String getMenusExcluding(FoodCategory category, List impossibilityMenus) { diff --git a/src/main/java/menu/domain/RecommendationFoodCategories.java b/src/main/java/menu/domain/RecommendationFoodCategories.java new file mode 100644 index 000000000..1423e6233 --- /dev/null +++ b/src/main/java/menu/domain/RecommendationFoodCategories.java @@ -0,0 +1,32 @@ +package menu.domain; + +import camp.nextstep.edu.missionutils.Randoms; +import java.util.ArrayList; +import java.util.List; + +public class RecommendationFoodCategories { + + private static final int MAX_CATEGORY_COUNT = 5; + private static final int MAX_CATEGORY_DUPLICATE = 2; + + private final List recommendationFoodCategories; + + public RecommendationFoodCategories() { + List categories = new ArrayList<>(); + while (categories.size() < MAX_CATEGORY_COUNT) { + FoodCategory category = FoodCategory.from( + Randoms.pickNumberInRange(FoodCategory.getFirstNumber(), FoodCategory.getLastNumber())); + List foodCategories = categories.stream() + .filter(existingCategory -> existingCategory == category) + .toList(); + if (foodCategories.size() < MAX_CATEGORY_DUPLICATE) { + categories.add(category); + } + } + this.recommendationFoodCategories = categories; + } + + public List getRecommendationFoodCategories() { + return recommendationFoodCategories; + } +} From 846eede0c1f6285fc5f77f02a18f39bcdba0bb40 Mon Sep 17 00:00:00 2001 From: coins117 Date: Sun, 21 Dec 2025 16:54:06 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feat:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/menu/MenuRecommendation.java | 2 +- src/main/java/menu/domain/ImpossibilityMenu.java | 4 ++++ src/main/java/menu/domain/ImpossibilityMenus.java | 5 ++--- src/main/java/menu/domain/MenuBoard.java | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/menu/MenuRecommendation.java b/src/main/java/menu/MenuRecommendation.java index 371051a35..c7dea5360 100644 --- a/src/main/java/menu/MenuRecommendation.java +++ b/src/main/java/menu/MenuRecommendation.java @@ -39,7 +39,7 @@ public void run() { for (FoodCategory category : categories) { for (Person person : persons.getPersons()) { while (true) { - List impossibilityMenu = impossibilityMenus.getImpossibilityMenu(person); + ImpossibilityMenu impossibilityMenu = impossibilityMenus.getImpossibilityMenu(person); String menu = MenuBoard.getMenusExcluding(category, impossibilityMenu); if (!recommendMenus.get(person).contains(menu)) { recommendMenus.get(person).add(menu); diff --git a/src/main/java/menu/domain/ImpossibilityMenu.java b/src/main/java/menu/domain/ImpossibilityMenu.java index eaa83b5aa..135e3294f 100644 --- a/src/main/java/menu/domain/ImpossibilityMenu.java +++ b/src/main/java/menu/domain/ImpossibilityMenu.java @@ -24,4 +24,8 @@ private void validate(List impossibilityMenus) { public List getImpossibilityMenus() { return impossibilityMenus; } + + public boolean contains(String menu) { + return impossibilityMenus.contains(menu); + } } diff --git a/src/main/java/menu/domain/ImpossibilityMenus.java b/src/main/java/menu/domain/ImpossibilityMenus.java index 70b50ca76..ef27ba00f 100644 --- a/src/main/java/menu/domain/ImpossibilityMenus.java +++ b/src/main/java/menu/domain/ImpossibilityMenus.java @@ -1,7 +1,6 @@ package menu.domain; import java.util.HashMap; -import java.util.List; import java.util.Map; public class ImpossibilityMenus { @@ -16,7 +15,7 @@ public void addImpossibilityMenu(Person person, ImpossibilityMenu menus) { impossibilityMenus.put(person, menus); } - public List getImpossibilityMenu(Person person) { - return impossibilityMenus.get(person).getImpossibilityMenus(); + public ImpossibilityMenu getImpossibilityMenu(Person person) { + return impossibilityMenus.get(person); } } diff --git a/src/main/java/menu/domain/MenuBoard.java b/src/main/java/menu/domain/MenuBoard.java index 83d61ad4f..3b3ee1d46 100644 --- a/src/main/java/menu/domain/MenuBoard.java +++ b/src/main/java/menu/domain/MenuBoard.java @@ -27,7 +27,7 @@ public static void validateMenuName(String menuName) { } } - public static String getMenusExcluding(FoodCategory category, List impossibilityMenus) { + public static String getMenusExcluding(FoodCategory category, ImpossibilityMenu impossibilityMenus) { List menus = menuBoard.get(category); while (true) { String menu = Randoms.shuffle(menus).get(0); From 60214fb249890ef7a6ed2dffd2c0c80db3b9e245 Mon Sep 17 00:00:00 2001 From: coins117 Date: Sun, 21 Dec 2025 16:59:53 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feat:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/menu/MenuRecommendation.java | 14 +++++++------- src/main/java/menu/view/OutputView.java | 3 +++ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/menu/MenuRecommendation.java b/src/main/java/menu/MenuRecommendation.java index c7dea5360..98cb76c92 100644 --- a/src/main/java/menu/MenuRecommendation.java +++ b/src/main/java/menu/MenuRecommendation.java @@ -38,14 +38,14 @@ public void run() { } for (FoodCategory category : categories) { for (Person person : persons.getPersons()) { - while (true) { - ImpossibilityMenu impossibilityMenu = impossibilityMenus.getImpossibilityMenu(person); - String menu = MenuBoard.getMenusExcluding(category, impossibilityMenu); - if (!recommendMenus.get(person).contains(menu)) { - recommendMenus.get(person).add(menu); - break; - } + ImpossibilityMenu impossibilityMenu = impossibilityMenus.getImpossibilityMenu(person); + String menu = null; + boolean recommended = true; + while (recommended) { + menu = MenuBoard.getMenusExcluding(category, impossibilityMenu); + recommended = !recommendMenus.get(person).contains(menu); } + recommendMenus.get(person).add(menu); } } outputView.printRecommendationResult(categories, recommendMenus); diff --git a/src/main/java/menu/view/OutputView.java b/src/main/java/menu/view/OutputView.java index 4bcc03564..53246436d 100644 --- a/src/main/java/menu/view/OutputView.java +++ b/src/main/java/menu/view/OutputView.java @@ -16,13 +16,16 @@ public void printStartApplication() { public void printRecommendationResult(List categories, Map> menus) { System.out.println("메뉴 추천 결과입니다."); System.out.println("[ 구분 | 월요일 | 화요일 | 수요일 | 목요일 | 금요일 ]"); + String categoryLine = String.join(" | ", categories.stream().map(FoodCategory::getName).collect(Collectors.toList())); System.out.println("[ 카테고리 | " + categoryLine + " ]"); + for (Person person : menus.keySet()) { String menuLine = String.join(" | ", menus.get(person)); System.out.println("[ " + person.getName() + " | " + menuLine + " ]"); } + System.out.println(); System.out.println("추천을 완료했습니다."); } From 99b2d214148108b1ea862212c56da12353be52d0 Mon Sep 17 00:00:00 2001 From: coins117 Date: Sun, 21 Dec 2025 17:11:22 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feat:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/menu/MenuRecommendation.java | 17 +++++-------- .../java/menu/domain/MenuRecommender.java | 24 +++++++++++++++++++ .../domain/RecommendationFoodCategories.java | 1 + 3 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 src/main/java/menu/domain/MenuRecommender.java diff --git a/src/main/java/menu/MenuRecommendation.java b/src/main/java/menu/MenuRecommendation.java index 98cb76c92..543f09050 100644 --- a/src/main/java/menu/MenuRecommendation.java +++ b/src/main/java/menu/MenuRecommendation.java @@ -8,7 +8,7 @@ import menu.domain.FoodCategory; import menu.domain.ImpossibilityMenu; import menu.domain.ImpossibilityMenus; -import menu.domain.MenuBoard; +import menu.domain.MenuRecommender; import menu.domain.Person; import menu.domain.Persons; import menu.domain.RecommendationFoodCategories; @@ -28,23 +28,18 @@ public MenuRecommendation(InputView inputView, OutputView outputView) { public void run() { outputView.printStartApplication(); Persons persons = retryOnError(this::getPersons); - ImpossibilityMenus impossibilityMenus = getImpossibilityMenus(persons); + MenuRecommender menuRecommender = getMenuRecommender(persons); RecommendationFoodCategories recommendationFoodCategories = new RecommendationFoodCategories(); List categories = recommendationFoodCategories.getRecommendationFoodCategories(); + Map> recommendMenus = new LinkedHashMap<>(); for (Person person : persons.getPersons()) { recommendMenus.put(person, new ArrayList<>()); } for (FoodCategory category : categories) { for (Person person : persons.getPersons()) { - ImpossibilityMenu impossibilityMenu = impossibilityMenus.getImpossibilityMenu(person); - String menu = null; - boolean recommended = true; - while (recommended) { - menu = MenuBoard.getMenusExcluding(category, impossibilityMenu); - recommended = !recommendMenus.get(person).contains(menu); - } + String menu = menuRecommender.recommendMenu(person, category, recommendMenus); recommendMenus.get(person).add(menu); } } @@ -66,13 +61,13 @@ private Persons getPersons() { return new Persons(personNames); } - private ImpossibilityMenus getImpossibilityMenus(Persons persons) { + private MenuRecommender getMenuRecommender(Persons persons) { ImpossibilityMenus impossibilityMenus = new ImpossibilityMenus(); for (Person person : persons.getPersons()) { ImpossibilityMenu menu = retryOnError(() -> getImpossibilityMenu(person)); impossibilityMenus.addImpossibilityMenu(person, menu); } - return impossibilityMenus; + return new MenuRecommender(impossibilityMenus); } private ImpossibilityMenu getImpossibilityMenu(Person person) { diff --git a/src/main/java/menu/domain/MenuRecommender.java b/src/main/java/menu/domain/MenuRecommender.java new file mode 100644 index 000000000..ef8f44288 --- /dev/null +++ b/src/main/java/menu/domain/MenuRecommender.java @@ -0,0 +1,24 @@ +package menu.domain; + +import java.util.List; +import java.util.Map; + +public class MenuRecommender { + + private final ImpossibilityMenus impossibilityMenus; + + public MenuRecommender(ImpossibilityMenus impossibilityMenus) { + this.impossibilityMenus = impossibilityMenus; + } + + public String recommendMenu(Person person, FoodCategory category, Map> recommendMenus) { + ImpossibilityMenu impossibilityMenu = impossibilityMenus.getImpossibilityMenu(person); + String menu = null; + boolean recommended = true; + while (recommended) { + menu = MenuBoard.getMenusExcluding(category, impossibilityMenu); + recommended = recommendMenus.get(person).contains(menu); + } + return menu; + } +} diff --git a/src/main/java/menu/domain/RecommendationFoodCategories.java b/src/main/java/menu/domain/RecommendationFoodCategories.java index 1423e6233..b8d4710f5 100644 --- a/src/main/java/menu/domain/RecommendationFoodCategories.java +++ b/src/main/java/menu/domain/RecommendationFoodCategories.java @@ -23,6 +23,7 @@ public RecommendationFoodCategories() { categories.add(category); } } + this.recommendationFoodCategories = categories; } From 4e5dab978276dc54323949acb82cd5b861be68c3 Mon Sep 17 00:00:00 2001 From: coins117 Date: Sun, 21 Dec 2025 17:17:57 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feat:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/menu/MenuRecommendation.java | 13 +++----- .../java/menu/domain/MenuRecommender.java | 7 ++--- src/main/java/menu/domain/RecommendMenus.java | 30 +++++++++++++++++++ 3 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 src/main/java/menu/domain/RecommendMenus.java diff --git a/src/main/java/menu/MenuRecommendation.java b/src/main/java/menu/MenuRecommendation.java index 543f09050..026f8fdf2 100644 --- a/src/main/java/menu/MenuRecommendation.java +++ b/src/main/java/menu/MenuRecommendation.java @@ -1,9 +1,6 @@ package menu; -import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.function.Supplier; import menu.domain.FoodCategory; import menu.domain.ImpossibilityMenu; @@ -11,6 +8,7 @@ import menu.domain.MenuRecommender; import menu.domain.Person; import menu.domain.Persons; +import menu.domain.RecommendMenus; import menu.domain.RecommendationFoodCategories; import menu.view.InputView; import menu.view.OutputView; @@ -33,17 +31,14 @@ public void run() { RecommendationFoodCategories recommendationFoodCategories = new RecommendationFoodCategories(); List categories = recommendationFoodCategories.getRecommendationFoodCategories(); - Map> recommendMenus = new LinkedHashMap<>(); - for (Person person : persons.getPersons()) { - recommendMenus.put(person, new ArrayList<>()); - } + RecommendMenus recommendMenus = new RecommendMenus(persons); for (FoodCategory category : categories) { for (Person person : persons.getPersons()) { String menu = menuRecommender.recommendMenu(person, category, recommendMenus); - recommendMenus.get(person).add(menu); + recommendMenus.addMenu(person, menu); } } - outputView.printRecommendationResult(categories, recommendMenus); + outputView.printRecommendationResult(categories, recommendMenus.getRecommendMenus()); } private T retryOnError(Supplier supplier) { diff --git a/src/main/java/menu/domain/MenuRecommender.java b/src/main/java/menu/domain/MenuRecommender.java index ef8f44288..708c07989 100644 --- a/src/main/java/menu/domain/MenuRecommender.java +++ b/src/main/java/menu/domain/MenuRecommender.java @@ -1,8 +1,5 @@ package menu.domain; -import java.util.List; -import java.util.Map; - public class MenuRecommender { private final ImpossibilityMenus impossibilityMenus; @@ -11,13 +8,13 @@ public MenuRecommender(ImpossibilityMenus impossibilityMenus) { this.impossibilityMenus = impossibilityMenus; } - public String recommendMenu(Person person, FoodCategory category, Map> recommendMenus) { + public String recommendMenu(Person person, FoodCategory category, RecommendMenus recommendMenus) { ImpossibilityMenu impossibilityMenu = impossibilityMenus.getImpossibilityMenu(person); String menu = null; boolean recommended = true; while (recommended) { menu = MenuBoard.getMenusExcluding(category, impossibilityMenu); - recommended = recommendMenus.get(person).contains(menu); + recommended = recommendMenus.containsMenu(person, menu); } return menu; } diff --git a/src/main/java/menu/domain/RecommendMenus.java b/src/main/java/menu/domain/RecommendMenus.java new file mode 100644 index 000000000..973782a6f --- /dev/null +++ b/src/main/java/menu/domain/RecommendMenus.java @@ -0,0 +1,30 @@ +package menu.domain; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class RecommendMenus { + + private final Map> recommendMenus; + + public RecommendMenus(Persons persons) { + this.recommendMenus = new LinkedHashMap<>(); + for (Person person : persons.getPersons()) { + recommendMenus.put(person, new ArrayList<>()); + } + } + + public boolean containsMenu(Person person, String menu) { + return recommendMenus.get(person).contains(menu); + } + + public void addMenu(Person person, String menu) { + recommendMenus.get(person).add(menu); + } + + public Map> getRecommendMenus() { + return recommendMenus; + } +} From 26225e335793c4049b3002957df1d811cb211ea4 Mon Sep 17 00:00:00 2001 From: coins117 Date: Sun, 21 Dec 2025 17:23:11 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feat:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/menu/domain/ImpossibilityMenu.java | 4 ---- src/main/java/menu/domain/Persons.java | 3 +-- src/main/java/menu/view/OutputView.java | 3 +-- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/main/java/menu/domain/ImpossibilityMenu.java b/src/main/java/menu/domain/ImpossibilityMenu.java index 135e3294f..583bcb8aa 100644 --- a/src/main/java/menu/domain/ImpossibilityMenu.java +++ b/src/main/java/menu/domain/ImpossibilityMenu.java @@ -21,10 +21,6 @@ private void validate(List impossibilityMenus) { } } - public List getImpossibilityMenus() { - return impossibilityMenus; - } - public boolean contains(String menu) { return impossibilityMenus.contains(menu); } diff --git a/src/main/java/menu/domain/Persons.java b/src/main/java/menu/domain/Persons.java index 3295cc29f..54fcf9f82 100644 --- a/src/main/java/menu/domain/Persons.java +++ b/src/main/java/menu/domain/Persons.java @@ -1,7 +1,6 @@ package menu.domain; import java.util.List; -import java.util.stream.Collectors; import menu.exception.ErrorCode; public class Persons { @@ -15,7 +14,7 @@ public Persons(List names) { validate(names); this.persons = names.stream() .map(Person::new) - .collect(Collectors.toUnmodifiableList()); + .toList(); } private void validate(List names) { diff --git a/src/main/java/menu/view/OutputView.java b/src/main/java/menu/view/OutputView.java index 53246436d..5f4f4236e 100644 --- a/src/main/java/menu/view/OutputView.java +++ b/src/main/java/menu/view/OutputView.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.Map; -import java.util.stream.Collectors; import menu.domain.FoodCategory; import menu.domain.Person; @@ -18,7 +17,7 @@ public void printRecommendationResult(List categories, Map