Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[점심 메뉴 추천] 박준수 미션 제출합니다. #6

Open
wants to merge 53 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
f85b2d7
chore: 기능구현/에러 처리 정의
GaBaljaintheroom Sep 17, 2023
22705ac
feat: InputView inputCoachName 작성
GaBaljaintheroom Sep 17, 2023
ba85652
feat: InputValidator validateContainComma 작성
GaBaljaintheroom Sep 17, 2023
5a32f96
feat: ErrorMessage 생성
GaBaljaintheroom Sep 17, 2023
d663d67
feat: ProgressMessage 생성
GaBaljaintheroom Sep 17, 2023
11d9ad5
feat: Coach 생성
GaBaljaintheroom Sep 17, 2023
0e6aab4
feat: Coaches 생성
GaBaljaintheroom Sep 17, 2023
d9641b0
feat: OutputView 생성
GaBaljaintheroom Sep 17, 2023
c01cb84
feat: ForbiddenMenu 생성
GaBaljaintheroom Sep 17, 2023
e250bc4
test: 입력한 값에 ,를 포함하고 있지 않으면 에러가 발생
GaBaljaintheroom Sep 17, 2023
25e6c48
test: 코치 이름이 2~4글자가 아니면 에러가 발생
GaBaljaintheroom Sep 17, 2023
4f1b0d7
test: 코치의 수가 2~5명 사이가 아니라면 에러가 발생
GaBaljaintheroom Sep 17, 2023
66fab2e
docs: 기능구현/에러처리 체크
GaBaljaintheroom Sep 17, 2023
3584097
feat: ErrorMessage COACH_FORBIDDEN_MENU_NUMBER_ERROR 추가
GaBaljaintheroom Sep 17, 2023
40dd036
feat: InputValidator validateForbiddenMenuNumber 추가
GaBaljaintheroom Sep 17, 2023
9dd2f45
feat: InputView inputForbiddenMenus 추가
GaBaljaintheroom Sep 17, 2023
d1c2237
feat: OutputView printProgressCoachForbiddenMessage 추가
GaBaljaintheroom Sep 17, 2023
74ac9d4
feat: ProgressMessage INPUT_COACH_FORBIDDEN_MENU 추가
GaBaljaintheroom Sep 17, 2023
a8ffda3
feat: Coach getName 추가
GaBaljaintheroom Sep 17, 2023
5a7ddca
test: 코치들의 못 먹는 음식이 최대 2가지가 아니라면 에러가 발생
GaBaljaintheroom Sep 17, 2023
6127800
feat: 코치들의 못 먹는 음식 추가
GaBaljaintheroom Sep 17, 2023
dfe8444
feat: OutputView printStartMenuRecommend 추가
GaBaljaintheroom Sep 17, 2023
b5c5d0d
feat: ProgressMessage START_MENU_RECOMMEND 추가
GaBaljaintheroom Sep 17, 2023
646de73
feat: MenuController 생성
GaBaljaintheroom Sep 17, 2023
85d3a87
feat: Categories 생성
GaBaljaintheroom Sep 17, 2023
ec22fb8
feat: Days 생성
GaBaljaintheroom Sep 17, 2023
bb6429b
feat: ErrorMessage CATEGORY_NUMBER_ERROR 작성
GaBaljaintheroom Sep 17, 2023
9fa38e8
feat: ScreenElement 생성
GaBaljaintheroom Sep 17, 2023
70c5a6e
feat: RecommendCategory randomCategory 작성
GaBaljaintheroom Sep 17, 2023
2a54266
refactor: RecommendCategory randomCategory 최대 2회 조건 추가
GaBaljaintheroom Sep 17, 2023
5b03dc3
feat: main함수 작성
GaBaljaintheroom Sep 17, 2023
58016b2
feat: Coach isNotContainRecommendMenuInForbidden 추가
GaBaljaintheroom Sep 17, 2023
f09205b
feat: CoachMenus 생성
GaBaljaintheroom Sep 17, 2023
1687622
feat: ErrorMessage MENU_TYPE_ERROR추가
GaBaljaintheroom Sep 17, 2023
459073c
feat: ForbiddenMenu isNotContainRecommendMenuInForbidden 추가
GaBaljaintheroom Sep 17, 2023
0555fbc
feat: Menus 생성
GaBaljaintheroom Sep 17, 2023
bf98464
feat: MeProgressMessage 메세지 추가
GaBaljaintheroom Sep 17, 2023
1fd8036
feat: RecommendMenu 생성
GaBaljaintheroom Sep 17, 2023
3b939d9
feat: OutputView printResultMenu 추가
GaBaljaintheroom Sep 17, 2023
60ddf2e
feat: RecommendCategory 메서드 추가
GaBaljaintheroom Sep 17, 2023
eb4ac64
feat: 출력형태 추가
GaBaljaintheroom Sep 17, 2023
07aa15f
feat: MenuService 생성
GaBaljaintheroom Sep 17, 2023
134c5b0
feat: MenuController recommendMenu 추가
GaBaljaintheroom Sep 17, 2023
60bf57e
refactor: CoachMenus addCoachMenu 메뉴 수정
GaBaljaintheroom Sep 17, 2023
083f5e3
feat: 패키지 분리
GaBaljaintheroom Sep 17, 2023
95c6387
refactor: 스트림으로 수정
GaBaljaintheroom Sep 17, 2023
0e65f41
test: 숫자에 맞는 카테고리를 찾는지 확인
GaBaljaintheroom Sep 17, 2023
cdd1454
test: 요일에 맞게 출력하는지 확인
GaBaljaintheroom Sep 17, 2023
c494f45
test: 타입에 맞는 음식 종류들을 찾을 수 있다
GaBaljaintheroom Sep 17, 2023
4ae70e5
test: 코치들이 저장이 되는지 확인
GaBaljaintheroom Sep 17, 2023
411e614
test: 못 먹는 음식을 저장하고 추천 음식이 포함되지 않으면 true를 반환한다.
GaBaljaintheroom Sep 17, 2023
41a1467
test: 카테고리가 최대 2번 중복되지 않았으면 추가되어 메뉴를 추천할 수 있다.
GaBaljaintheroom Sep 17, 2023
d88f924
docs: 리드미 업데이트
GaBaljaintheroom Sep 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
## 기능 구현

- InputView
- [x] 코치의 이름을 입력받는다.
- [x] 코치의 이름은 최소 2글자, 최대 4글자이다.
- [x] 먹지 못하는 메뉴가 없으면 빈 값을 입력한다.

- Coach
- [x] 코치 이름의 길이는 2~4글자 사이다.
- [x] 각 코치는 최소 0개, 최대 2개의 못 먹는 메뉴가 있다.

- Coaches
- [x] 코치는 최소 2명, 최대 5명까지 식사를 함께 한다.
- 각 코치들의 못 먹는 메뉴를 저장한다.

- CoachMenus
- [x] 각 코치에게 한 주에 중복되지 않는 메뉴를 추천해야 한다.

- Category
- [x] 1이면 일식, 2면 한식, 3이면 중식, 4면 아시안, 5면 양식을 추천한다.

- ForbiddenMenu
- [x] 못 먹는 음식을 저장한다.

- RecommendCategory
- [x] 한 주에 같은 카테고리는 최대 2회까지만 고를 수 있다.

- RecommendMenu
- [x] 카테고리에 맞는 메뉴를 생성한다,

- Menus
- [x] 카테고리에 포함되는 메뉴 목록을 List<String> 형태로 반환한다.

- OutputView
- 메뉴 추천 결과를 출력한다.



## 에러 처리

- InputValidate
- [x] 사용자가 잘못된 값을 입력할 경우 IllegalArgumentException를 발생시키고, "[ERROR]"로 시작하는 에러 메시지를 출력 후 그 부분부터 입력을 다시 받는다.
Exception이 아닌 IllegalArgumentException, IllegalStateException 등과 같은 명확한 유형을 처리한다.
- [x] , 로 구분해서 입력한다.
- [x] 못 먹는 음식은 코치별 0~2개 이다.
5 changes: 4 additions & 1 deletion src/main/java/menu/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package menu;

import menu.controller.MenuController;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
MenuController menuController = new MenuController();
menuController.run();
}
}
27 changes: 27 additions & 0 deletions src/main/java/menu/constants/Categories.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package menu.constants;

import java.util.Arrays;

public enum Categories {
JAPANESE(1, "일식"),
KOREAN(2, "한식"),
CHINESE(3, "중식"),
AISIAN(4, "아시안"),
WESTERN(5, "양식");

private final int number;
private final String type;

Categories(int number, String type) {
this.number = number;
this.type = type;
}

public static String findCategory(int number) {
return Arrays.stream(values())
.filter(categories -> categories.number == number)
.map(categories -> categories.type)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(ErrorMessage.CATEGORY_NUMBER_ERROR.getMessage()));
}
}
30 changes: 30 additions & 0 deletions src/main/java/menu/constants/Days.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package menu.constants;

public enum Days {
MONDAY("월요일"),
TUESDAY("화요일"),
WEDNESDAY("수요일"),
THURSDAY("목요일"),
FRIDAY("금요일");

private static final String TAG = "구분";
private final String day;

Days(String day) {
this.day = day;
}

public static String showDivisionDays() {
StringBuilder sb = new StringBuilder();
sb.append(ScreenElement.FIRST_ELEMENT).append(TAG);
for (Days days : Days.values()) {
sb.append(ScreenElement.MIDDLE_DIVISION).append(days.getDay());
}
sb.append(ScreenElement.LAST_ELEMENT);
return sb.toString();
}

public String getDay() {
return day;
}
}
21 changes: 21 additions & 0 deletions src/main/java/menu/constants/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package menu.constants;

public enum ErrorMessage {
DIVISION_COMMA_ERROR("(, 로 구분)되지 않았습니다."),
COACH_NAME_LENGTH_ERROR("코치 이름은 최소 2글자, 최대 4글자까지 입력가능 합니다."),
COACH_NUMBER_ERROR("코치는 최소 2명, 최대 5명자까지 입력가능 합니다."),
COACH_FORBIDDEN_MENU_NUMBER_ERROR("각 코치가 못 먹는 메뉴는 최소 0개, 최대 2개의 입니다."),
CATEGORY_NUMBER_ERROR("존재하지 않는 카테고리 번호입니다."),
MENU_TYPE_ERROR("존재하지 않는 음식종류 입니다.");

private static final String error = "[ERROR] ";
private final String message;

ErrorMessage(String message) {
this.message = message;
}

public String getMessage() {
return error + message;
}
}
34 changes: 34 additions & 0 deletions src/main/java/menu/constants/Menus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package menu.constants;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public enum Menus {
JAPANESE("일식", List.of("규동", "우동", "미소시루", "스시", "가츠동", "오니기리", "하이라이스", "라멘", "오코노미야끼")),
KOREAN("한식", List.of("김밥", "김치찌개", "쌈밥", "된장찌개", "비빔밥", "칼국수", "불고기", "떡볶이", "제육볶음")),
CHINES("중식", List.of("깐풍기", "볶음면", "동파육", "짜장면", "짬뽕", "마파두부", "탕수육", "토마토 달걀볶음", "고추잡채")),
ASIAN("아시안", List.of("팟타이", "카오 팟", "나시고렝", "파인애플 볶음밥", "쌀국수", "똠얌꿍", "반미", "월남쌈", "분짜")),
WESTERN("양식", List.of("라자냐", "그라탱", "뇨끼", "끼슈", "프렌치 토스트", "바게트", "스파게티", "피자", "파니니"));

private final String type;
private final List<String> foods;

Menus(String type, List<String> foods) {
this.type = type;
this.foods = foods;
}

public static List<String> findFoods(String type) {
return Arrays.stream(Menus.values())
.filter(isSameType(type))
.map(menus -> menus.foods)
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(ErrorMessage.MENU_TYPE_ERROR.getMessage()));
}

private static Predicate<Menus> isSameType(String type) {
return menus -> menus.type.equals(type);
}

}
23 changes: 23 additions & 0 deletions src/main/java/menu/constants/ProgressMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package menu.constants;

public enum ProgressMessage {
START_MENU_RECOMMEND("점심 메뉴 추천을 시작합니다."),
INPUT_COACH_NAME("코치의 이름을 입력해 주세요. (, 로 구분)"),
INPUT_COACH_FORBIDDEN_MENU("%s(이)가 못 먹는 메뉴를 입력해 주세요."),
RECOMMEND_MENU_RESULT("메뉴 추천 결과입니다."),
FINISH_RECOMMEND("추천을 완료했습니다.");
private final String message;

ProgressMessage(String message) {
this.message = message;
}

public String getMessage() {
return message;
}

@Override
public String toString() {
return message;
}
}
21 changes: 21 additions & 0 deletions src/main/java/menu/constants/ScreenElement.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package menu.constants;

public enum ScreenElement {
FIRST_ELEMENT("[ "),
MIDDLE_DIVISION(" | "),
LAST_ELEMENT(" ]");
private final String value;

ScreenElement(String value) {
this.value = value;
}

public String getValue() {
return value;
}

@Override
public String toString() {
return value;
}
}
41 changes: 41 additions & 0 deletions src/main/java/menu/controller/MenuController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package menu.controller;

import menu.domain.Coaches;
import menu.service.MenuService;
import menu.view.InputView;
import menu.view.OutputView;

public class MenuController {

private final MenuService menuService;
private final InputView inputView;
private final OutputView outputView;

public MenuController() {
this.menuService = new MenuService();
this.inputView = new InputView();
this.outputView = new OutputView();
}

public void run() {
outputView.printStartMenuRecommend();
Coaches coaches = inputView.inputCoachName();
addCoachesForbiddenMenu(coaches);

recommendMenu(coaches);
outputView.printResultMenu(menuService.showMenuResult());
}

private void addCoachesForbiddenMenu(Coaches coaches) {
coaches.getCoaches().forEach(coach -> {
String menu = inputView.inputForbiddenMenus(coach);
coach.addForbiddenMenu(menu);
});
}

private void recommendMenu(Coaches coaches) {
while (menuService.isRecommending()) {
menuService.addRecommendMenu(coaches);
}
}
}
35 changes: 35 additions & 0 deletions src/main/java/menu/domain/Coach.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package menu.domain;

import menu.constants.ErrorMessage;

public class Coach {

private static final int MIN_NAME_LENGTH = 2;
private static final int MAX_NAME_LENGTH = 4;
private final ForbiddenMenu forbiddenMenu;
private final String name;

public Coach(String name) {
validateCoachNameLength(name);
this.name = name;
this.forbiddenMenu = new ForbiddenMenu();
}

private void validateCoachNameLength(String name) {
if (name.length() < MIN_NAME_LENGTH || name.length() > MAX_NAME_LENGTH) {
throw new IllegalArgumentException(ErrorMessage.COACH_NAME_LENGTH_ERROR.getMessage());
}
}

public void addForbiddenMenu(String menu) {
forbiddenMenu.addForbiddenMenu(menu);
}

public boolean isNotContainRecommendMenuInForbidden(String menu) {
return forbiddenMenu.isNotContainRecommendMenuInForbidden(menu);
}

public String getName() {
return name;
}
}
51 changes: 51 additions & 0 deletions src/main/java/menu/domain/CoachMenus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package menu.domain;

import menu.constants.ScreenElement;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class CoachMenus {
private final RecommendMenu recommendMenu;
private final Map<Coach, List<String>> coachMenus;

public CoachMenus() {
this.recommendMenu = new RecommendMenu();
this.coachMenus = new HashMap<>();
}

public void addCoachMenu(Coach coach, List<String> menus) {
while (true) {
String menu = recommendMenu.getMenu(menus);
List<String> recommendMenus = coachMenus.getOrDefault(coach, new ArrayList<>());
if (canRecommendMenu(coach, menu)) {
recommendMenus.add(menu);
coachMenus.put(coach, recommendMenus);
break;
}
}
}

private boolean canRecommendMenu(Coach coach, String menu) {
return coach.isNotContainRecommendMenuInForbidden(menu) && isNotDuplicateMenu(coach, menu);
}

private boolean isNotDuplicateMenu(Coach coach, String menu) {
List<String> menus = coachMenus.getOrDefault(coach, new ArrayList<>());
return !menus.contains(menu);
}

public String showCoachMenus() {
return coachMenus.keySet().stream()
.map(coach -> {
String menus = coachMenus.get(coach).stream()
.collect(Collectors.joining(ScreenElement.MIDDLE_DIVISION.getValue()));
return ScreenElement.FIRST_ELEMENT + coach.getName()
+ ScreenElement.MIDDLE_DIVISION + menus + ScreenElement.LAST_ELEMENT;
})
.collect(Collectors.joining("\n")) + "\n";
}
}
40 changes: 40 additions & 0 deletions src/main/java/menu/domain/Coaches.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package menu.domain;

import menu.constants.ErrorMessage;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static menu.view.InputValidator.DIVISION;

public class Coaches {

private static final int MIN_COACH_NUMBER = 2;
private static final int MAX_COACH_NUMBER = 5;
private final List<Coach> coaches = new ArrayList<>();

public Coaches(String names) {
String[] coachNames = names.split(DIVISION);
validateCoachNumber(coachNames.length);
addCoaches(coachNames);
}

private void validateCoachNumber(int number) {
if (number < MIN_COACH_NUMBER || number > MAX_COACH_NUMBER) {
throw new IllegalArgumentException(ErrorMessage.COACH_NUMBER_ERROR.getMessage());
}
}

private void addCoaches(String[] coachNames) {
coaches.addAll(Arrays.stream(coachNames)
.map(String::trim)
.map(Coach::new)
.collect(Collectors.toList()));
}

public List<Coach> getCoaches() {
return coaches;
}
}
Loading