diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..31091dde8 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,41 @@ +# 페어매칭 프로그램 + +## 개발 기능 목록 + +- [x] 기능 명령어 선택 기능 + - [x] 문자 한 개를 입력하는지 검증 + +- [x] 기능 명령어 검증 기능 + - [x] 1, 2, 3, Q 중 하나인지 검증 + +- [x] 명령어 실행 기능 + - [x] 명령어가 입력되면 각 명령에 맞는 기능 실행 + +- [x] 과정, 레벨, 미션 선택 기능 + - [x] ", "로 구분되는 문자열인지 검증 + +- [x] 과정, 레벨, 미션 검증 기능 + - [x] 각 보기에 부합하는지 검증 + +- [x] 매칭 이력 확인 기능 + +- [x] 매칭 기능 + - [x] 파일 읽기 + - [x] 크루원 이름 셔플 + - [x] 2명(마지막 3명 허용)씩 매칭 + +- [x] 매칭 검증 기능 + - [x] 같은 레벨 하 매칭 중복 검증 + - [x] 매칭 실패 시 오류 발생 + - 매칭 실패 경우 + - [ ] 이름 중복 확인 기능 + +- [x] 재매칭 선택 및 검증 기능 + - [x] 네, 아니오 중 하나인지 검증 + - [ ] 재매칭 아니오 선택 후 출력문 변화 + +- [x] 매칭 조회 기능 + - [x] 매칭 이력 없을 경우 오류 + - [x] 매칭 이력 있는 경우 출력 + +- [x] 매칭 초기화 기능 \ No newline at end of file diff --git a/src/main/java/pairmatching/Application.java b/src/main/java/pairmatching/Application.java index 6f56e741c..a8e5c0085 100644 --- a/src/main/java/pairmatching/Application.java +++ b/src/main/java/pairmatching/Application.java @@ -1,7 +1,12 @@ package pairmatching; +import pairmatching.controller.MainController; +import pairmatching.view.InputView; +import pairmatching.view.OutputView; + public class Application { public static void main(String[] args) { - // TODO 구현 진행 + MainController mainController = new MainController(new InputView(), new OutputView()); + mainController.runMain(); } } diff --git a/src/main/java/pairmatching/controller/InputController.java b/src/main/java/pairmatching/controller/InputController.java new file mode 100644 index 000000000..4d0a4ca8a --- /dev/null +++ b/src/main/java/pairmatching/controller/InputController.java @@ -0,0 +1,76 @@ +package pairmatching.controller; + +import pairmatching.controller.command.MainCommand; +import pairmatching.controller.command.ReMatchingCommand; +import pairmatching.domain.choice.Choice; +import pairmatching.domain.choice.ChoiceMaker; +import pairmatching.view.InputView; +import pairmatching.view.OutputView; + +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class InputController { + + private final InputView inputView; + private final OutputView outputView; + + public InputController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + } + + public MainCommand readValidCommand() { + return repeatUntilGettingValidValue(this::readCommand); + } + + public Choice readValidChoice() { + return repeatUntilGettingValidValue(this::readChoice); + } + + public ReMatchingCommand readValidReMatchingCommand() { + return repeatUntilGettingValidValue(this::readReMatchingCommand); + } + + private MainCommand readCommand() { + outputView.printCommandGuide(); + String inputCommand = inputView.readCommand(); + return MainCommand.valueOfCommand(inputCommand); + } + + private Choice readChoice() { + outputView.printChoiceGuide(); + List choices = inputView.readChoices(); + ChoiceMaker choiceMaker = new ChoiceMaker(); + return choiceMaker.createChoice(choices); + } + + private ReMatchingCommand readReMatchingCommand() { + outputView.printReMatchingGuide(); + String input = inputView.readReMatchingCommand(); + return ReMatchingCommand.valueOfReMatchingCommand(input); + } + + private T repeatUntilGettingValidValue(Supplier getSomething) { + while (true) { + try { + return getSomething.get(); + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } + + public void repeatUntilGettingValidValue(Consumer getSomething, T input) { + boolean isContinuing = true; + while (isContinuing) { + try { + getSomething.accept(input); + isContinuing = false; + } catch (IllegalArgumentException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + } +} diff --git a/src/main/java/pairmatching/controller/MainController.java b/src/main/java/pairmatching/controller/MainController.java new file mode 100644 index 000000000..d00c96d1a --- /dev/null +++ b/src/main/java/pairmatching/controller/MainController.java @@ -0,0 +1,94 @@ +package pairmatching.controller; + +import pairmatching.controller.command.MainCommand; +import pairmatching.controller.command.ReMatchingCommand; +import pairmatching.domain.choice.Choice; +import pairmatching.domain.crew.Crew; +import pairmatching.domain.matching.MatchingHistory; +import pairmatching.domain.matching.MatchingProgram; +import pairmatching.domain.matching.PairMatchingMachine; +import pairmatching.view.InputView; +import pairmatching.view.OutputView; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +public class MainController { + + private final InputView inputView; + private final OutputView outputView; + private final InputController inputController; + + public MainController(InputView inputView, OutputView outputView) { + this.inputView = inputView; + this.outputView = outputView; + this.inputController = new InputController(inputView, outputView); + } + + public void runMain() { + try { + runProgram(); + } catch (IOException e) { + outputView.printErrorMessage(e.getMessage()); + } + } + + private void runProgram() throws IOException { + executeCommand(); + } + + private void executeCommand() throws IOException { + MainCommand command; + MatchingProgram program = new MatchingProgram(new MatchingHistory(), new PairMatchingMachine()); + do { + command = inputController.readValidCommand(); + decideAndExecute(command, program); + } while (!command.isCommandOf(MainCommand.QUITTING)); + } + + private void decideAndExecute(MainCommand command, MatchingProgram program) throws IOException { + if (command.isCommandOf(MainCommand.MATCHING)) { + executeMatchingCommand(program); + } + if (command.isCommandOf(MainCommand.CHECKING)) { + executeCheckingCommand(program); + } + if (command.isCommandOf(MainCommand.INITIALIZING)) { + executeInitializingCommand(program); + } + } + + private void executeMatchingCommand(MatchingProgram program) throws IOException { + List> pairs = new ArrayList<>(); + while (pairs.isEmpty()) { + Choice choice = inputController.readValidChoice(); + if (program.hasMatched(choice)) { + executeReMatchingCommand(program, choice); + } + if (!program.hasMatched(choice)) { + pairs = program.matchAndRecord(choice); + } + } + outputView.printMatchingResult(pairs); + } + + private void executeReMatchingCommand(MatchingProgram program, Choice choice) throws IOException { + ReMatchingCommand command = inputController.readValidReMatchingCommand(); + if (command.isCommandOf(ReMatchingCommand.RE_MATCHING)) { + program.deleteHistory(choice); + } + } + + private void executeCheckingCommand(MatchingProgram program) { + Choice choice = inputController.readValidChoice(); + List> pairs = program.show(choice); + outputView.printMatchingResult(pairs); + } + + private void executeInitializingCommand(MatchingProgram program) { + program.truncateHistory(); + outputView.printInitializingMessage(); + } +} diff --git a/src/main/java/pairmatching/controller/command/MainCommand.java b/src/main/java/pairmatching/controller/command/MainCommand.java new file mode 100644 index 000000000..379ece5d1 --- /dev/null +++ b/src/main/java/pairmatching/controller/command/MainCommand.java @@ -0,0 +1,32 @@ +package pairmatching.controller.command; + +import java.util.Arrays; + +public enum MainCommand { + MATCHING("1"), + CHECKING("2"), + INITIALIZING("3"), + QUITTING("Q"); + + private static final String ERROR_MESSAGE = "[ERROR] %s는 기능이 아닙니다.\n"; + private final String command; + + MainCommand(String command) { + this.command = command; + } + + public static MainCommand valueOfCommand(String inputCommand) { + return Arrays.stream(values()) + .filter(value -> inputCommand.equals(value.getCommand())) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(String.format(ERROR_MESSAGE, inputCommand))); + } + + public String getCommand() { + return command; + } + + public boolean isCommandOf(MainCommand command) { + return this.command.equals(command.getCommand()); + } +} diff --git a/src/main/java/pairmatching/controller/command/ReMatchingCommand.java b/src/main/java/pairmatching/controller/command/ReMatchingCommand.java new file mode 100644 index 000000000..76efea265 --- /dev/null +++ b/src/main/java/pairmatching/controller/command/ReMatchingCommand.java @@ -0,0 +1,30 @@ +package pairmatching.controller.command; + +import java.util.Arrays; + +public enum ReMatchingCommand { + RE_MATCHING("네"), + NON_RE_MATCHING("아니오"); + + private static final String ERROR_MESSAGE = "[ERROR] %s는 재매칭 명령어가 아닙니다.\n"; + private final String command; + + ReMatchingCommand(String command) { + this.command = command; + } + + public static ReMatchingCommand valueOfReMatchingCommand(String inputCommand) { + return Arrays.stream(values()) + .filter(value -> inputCommand.equals(value.getCommand())) + .findAny() + .orElseThrow(() -> new IllegalArgumentException(String.format(ERROR_MESSAGE, inputCommand))); + } + + public String getCommand() { + return command; + } + + public boolean isCommandOf(ReMatchingCommand command) { + return this.command.equals(command.getCommand()); + } +} \ No newline at end of file diff --git a/src/main/java/pairmatching/domain/choice/Choice.java b/src/main/java/pairmatching/domain/choice/Choice.java new file mode 100644 index 000000000..fb872f933 --- /dev/null +++ b/src/main/java/pairmatching/domain/choice/Choice.java @@ -0,0 +1,42 @@ +package pairmatching.domain.choice; + +import pairmatching.domain.item.Course; +import pairmatching.domain.item.Mission; + +import java.util.Objects; + +public class Choice { + + private final Course course; + private final Mission mission; + + public Choice(Course course, Mission mission) { + this.course = course; + this.mission = mission; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Choice choice = (Choice) o; + return course == choice.course && mission == choice.mission; + } + + @Override + public int hashCode() { + return Objects.hash(course, mission); + } + + public boolean hasCourseOf(Course course) { + return this.course.equals(course); + } + + public boolean hasSameLevel(Choice choice) { + return mission.isSameLevel(choice.mission); + } +} diff --git a/src/main/java/pairmatching/domain/choice/ChoiceMaker.java b/src/main/java/pairmatching/domain/choice/ChoiceMaker.java new file mode 100644 index 000000000..1ff376b1b --- /dev/null +++ b/src/main/java/pairmatching/domain/choice/ChoiceMaker.java @@ -0,0 +1,17 @@ +package pairmatching.domain.choice; + +import pairmatching.domain.item.Course; +import pairmatching.domain.item.Level; +import pairmatching.domain.item.Mission; + +import java.util.List; + +public class ChoiceMaker { + + public Choice createChoice(List choices) { + Course course = Course.valueOfCourse(choices.get(0)); + Level level = Level.valueOfLevel(choices.get(1)); + Mission mission = Mission.valueOfMissionAndLevel(choices.get(2), level); + return new Choice(course, mission); + } +} diff --git a/src/main/java/pairmatching/domain/crew/Crew.java b/src/main/java/pairmatching/domain/crew/Crew.java new file mode 100644 index 000000000..fd02e2516 --- /dev/null +++ b/src/main/java/pairmatching/domain/crew/Crew.java @@ -0,0 +1,26 @@ +package pairmatching.domain.crew; + +import pairmatching.domain.item.Course; + +public class Crew { + + private final Course course; + private final String name; + + public Crew(Course course, String name) { + this.course = course; + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return "Crew{" + + "course=" + course + + ", name='" + name + '\'' + + '}'; + } +} \ No newline at end of file diff --git a/src/main/java/pairmatching/domain/item/Course.java b/src/main/java/pairmatching/domain/item/Course.java new file mode 100644 index 000000000..0f7fc0154 --- /dev/null +++ b/src/main/java/pairmatching/domain/item/Course.java @@ -0,0 +1,26 @@ +package pairmatching.domain.item; + +import java.util.Arrays; + +public enum Course { + BACKEND("백엔드"), + FRONTEND("프론트엔드"); + + private static final String ERROR_MESSAGE = "[ERROR] %s 과정은 존재하지 않습니다.\n"; + private final String courseName; + + Course(String courseName) { + this.courseName = courseName; + } + + public static Course valueOfCourse(String name) { + return Arrays.stream(values()) + .filter(value -> name.equals(value.getCourseName())) + .findAny() + .orElseThrow(() -> new IllegalStateException(String.format(ERROR_MESSAGE, name))); + } + + public String getCourseName() { + return courseName; + } +} diff --git a/src/main/java/pairmatching/domain/item/Level.java b/src/main/java/pairmatching/domain/item/Level.java new file mode 100644 index 000000000..eff5ef793 --- /dev/null +++ b/src/main/java/pairmatching/domain/item/Level.java @@ -0,0 +1,29 @@ +package pairmatching.domain.item; + +import java.util.Arrays; + +public enum Level { + LEVEL1("레벨1"), + LEVEL2("레벨2"), + LEVEL3("레벨3"), + LEVEL4("레벨4"), + LEVEL5("레벨5"); + + private static final String ERROR_MESSAGE = "[ERROR] %s 레벨은 존재하지 않습니다.\n"; + private final String levelName; + + Level(String levelName) { + this.levelName = levelName; + } + + public static Level valueOfLevel(String name) { + return Arrays.stream(values()) + .filter(value -> name.equals(value.getLevelName())) + .findAny() + .orElseThrow(() -> new IllegalStateException(String.format(ERROR_MESSAGE, name))); + } + + public String getLevelName() { + return levelName; + } +} diff --git a/src/main/java/pairmatching/domain/item/Mission.java b/src/main/java/pairmatching/domain/item/Mission.java new file mode 100644 index 000000000..62db7195d --- /dev/null +++ b/src/main/java/pairmatching/domain/item/Mission.java @@ -0,0 +1,51 @@ +package pairmatching.domain.item; + +import java.util.Arrays; + +public enum Mission { + CAR_RACING("자동차경주", Level.LEVEL1), + LOTTO("로또", Level.LEVEL1), + NUMBER_BASEBALL("숫자야구게임", Level.LEVEL1), + SHOPPING_BASKET("장바구니", Level.LEVEL2), + PAYMENT("결제", Level.LEVEL2), + SUBWAY_MAP("지하철노선도", Level.LEVEL2), + PERFORMANCE_IMPROVEMENT("성능개선", Level.LEVEL3), + PUBLISHING("배포", Level.LEVEL3); + + private static final String DISCORDANCE_ERROR_MESSAGE = "[ERROR] %s 미션은 \"%s\"이 아닙니다.\n"; + private static final String VALUE_ERROR_MESSAGE = "[ERROR] %s 미션은 존재하지 않습니다.\n"; + private final String missionName; + private final Level level; + + Mission(String missionName, Level level) { + this.missionName = missionName; + this.level = level; + } + + public static Mission valueOfMissionAndLevel(String missionName, Level level) { + Mission mission = valueOfMission(missionName); + if (!level.equals(mission.getLevel())) { + throw new IllegalArgumentException(String.format(DISCORDANCE_ERROR_MESSAGE, missionName, level.getLevelName())); + } + return mission; + } + + public static Mission valueOfMission(String name) { + return Arrays.stream(values()) + .filter(value -> name.equals(value.getMissionName())) + .findAny() + .orElseThrow(() -> new IllegalStateException(String.format(VALUE_ERROR_MESSAGE, name))); + } + + public String getMissionName() { + return missionName; + } + + public Level getLevel() { + return level; + } + + public boolean isSameLevel(Mission mission) { + return level.equals(mission.getLevel()); + } +} diff --git a/src/main/java/pairmatching/domain/matching/MatchingHistory.java b/src/main/java/pairmatching/domain/matching/MatchingHistory.java new file mode 100644 index 000000000..6da532d37 --- /dev/null +++ b/src/main/java/pairmatching/domain/matching/MatchingHistory.java @@ -0,0 +1,51 @@ +package pairmatching.domain.matching; + +import pairmatching.domain.choice.Choice; +import pairmatching.domain.crew.Crew; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +public class MatchingHistory { + private final Map>> history; + + public MatchingHistory() { + this.history = new HashMap<>(); + } + + public boolean hasMatchingOf(Choice choice) { + return history.containsKey(choice); + } + + public void record(Choice choice, List> pairs) { + history.put(choice, pairs); + } + + public boolean hasDuplicatePairInSameLevel(Choice choice, List> pairs) { + return history.keySet().stream() + .filter(pastChoice -> pastChoice.hasSameLevel(choice)) + .noneMatch(pastChoice -> hasSamePair(pastChoice, pairs)); + } + + private boolean hasSamePair(Choice choice, List> pairs) { + List> pastPairs = history.get(choice); + return pastPairs.stream() + .noneMatch(pair -> pairs.stream() + .anyMatch(Predicate.isEqual(pair))); + } + + public void delete(Choice choice) { + history.remove(choice); + } + + public List> getRecord(Choice choice) { + return history.get(choice); + } + + public void truncate() { + history.clear(); + } +} diff --git a/src/main/java/pairmatching/domain/matching/MatchingProgram.java b/src/main/java/pairmatching/domain/matching/MatchingProgram.java new file mode 100644 index 000000000..3e944eac9 --- /dev/null +++ b/src/main/java/pairmatching/domain/matching/MatchingProgram.java @@ -0,0 +1,64 @@ +package pairmatching.domain.matching; + +import pairmatching.domain.choice.Choice; +import pairmatching.domain.crew.Crew; + +import java.io.IOException; +import java.util.List; +import java.util.Set; + +public class MatchingProgram { + + private static final int MINIMUM_COUNT = 0; + private static final int MAXIMUM_COUNT = 3; + private static final String UN_MATCHED_ERROR_MESSAGE = "[ERROR] 매칭 시도가 %d회를 초과하였습니다.\n"; + private static final String UN_MATCHED_CHOICE_ERROR_MESSAGE = "[ERROR] 매칭 이력이 없습니다.\n"; + private final MatchingHistory history; + private final PairMatchingMachine pairMatchingMachine; + + public MatchingProgram(MatchingHistory history, PairMatchingMachine pairMatchingMachine) { + this.history = history; + this.pairMatchingMachine = pairMatchingMachine; + } + + public boolean hasMatched(Choice choice) { + return history.hasMatchingOf(choice); + } + + public List> matchAndRecord(Choice choice) throws IOException { + List> pairs; + int count = MINIMUM_COUNT; + do { + pairs = pairMatchingMachine.makePairs(choice); + count++; + validateMatchingCount(count); + } while (!history.hasDuplicatePairInSameLevel(choice, pairs)); + history.record(choice, pairs); + return pairs; + } + + private void validateMatchingCount(int count) { + if (count == MAXIMUM_COUNT) { + throw new IllegalStateException(String.format(UN_MATCHED_ERROR_MESSAGE, count)); + } + } + + public void deleteHistory(Choice choice) { + history.delete(choice); + } + + public List> show(Choice choice) { + validate(choice); + return history.getRecord(choice); + } + + private void validate(Choice choice) { + if (!hasMatched(choice)) { + throw new IllegalArgumentException(UN_MATCHED_CHOICE_ERROR_MESSAGE); + } + } + + public void truncateHistory() { + history.truncate(); + } +} diff --git a/src/main/java/pairmatching/domain/matching/PairMatchingMachine.java b/src/main/java/pairmatching/domain/matching/PairMatchingMachine.java new file mode 100644 index 000000000..99d15b842 --- /dev/null +++ b/src/main/java/pairmatching/domain/matching/PairMatchingMachine.java @@ -0,0 +1,66 @@ +package pairmatching.domain.matching; + +import camp.nextstep.edu.missionutils.Randoms; +import pairmatching.domain.choice.Choice; +import pairmatching.domain.crew.Crew; +import pairmatching.domain.item.Course; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class PairMatchingMachine { + + private static final String BACKEND_CREW_PATH = "C:\\programming\\woowacourse\\practice\\java-pairmatching-precourse\\src\\main\\resources\\backend-crew.md"; + private static final String FRONTEND_CREW_PATH = "C:\\programming\\woowacourse\\practice\\java-pairmatching-precourse\\src\\main\\resources\\frontend-crew.md"; + private static final int BASIC_PAIR_SIZE = 2; + private static final int SPECIAL_PAIR_SIZE = 3; + private static final int START_NUMBER = 1; + + public List> makePairs(Choice choice) throws IOException { + if (choice.hasCourseOf(Course.BACKEND)) { + List shuffledCrews = readShuffledCrews(BACKEND_CREW_PATH); + return makePairsBy(shuffledCrews); + } + List shuffledCrews = readShuffledCrews(FRONTEND_CREW_PATH); + return makePairsBy(shuffledCrews); + } + + private List> makePairsBy(List shuffledCrews) { + List> pairs = new ArrayList<>(); + int halfSize = shuffledCrews.size() / BASIC_PAIR_SIZE; + for (int i = 0; i < halfSize; i++) { + pairs.add(makeCrewPairByOrder(shuffledCrews, halfSize, i)); + } + return pairs; + } + + private Set makeCrewPairByOrder(List shuffledCrews, int halfSize, int order) { + if (order == halfSize - START_NUMBER) { + return makePair(shuffledCrews, order * BASIC_PAIR_SIZE, SPECIAL_PAIR_SIZE); + } + return makePair(shuffledCrews, order * BASIC_PAIR_SIZE, BASIC_PAIR_SIZE); + } + + private static Set makePair(List shuffledCrews, int skipSize, int pairSize) { + return shuffledCrews.stream() + .skip(skipSize) + .limit(pairSize) + .collect(Collectors.toSet()); + } + + private List readShuffledCrews(String path) throws IOException { + List shuffledNames = Randoms.shuffle(readNames(path)); + return shuffledNames.stream() + .map(name -> new Crew(Course.BACKEND, name)) + .collect(Collectors.toList()); + } + + public List readNames(String path) throws IOException { + return Files.readAllLines(Paths.get(path)); + } +} diff --git a/src/main/java/pairmatching/view/InputView.java b/src/main/java/pairmatching/view/InputView.java new file mode 100644 index 000000000..6eb451981 --- /dev/null +++ b/src/main/java/pairmatching/view/InputView.java @@ -0,0 +1,35 @@ +package pairmatching.view; + +import camp.nextstep.edu.missionutils.Console; +import pairmatching.view.validator.Validator; + +import java.util.Arrays; +import java.util.List; + +public class InputView { + + private static final String CHOICE_DELIMITER = ", "; + + public String readCommand() { + return readValidInputBy(Validator.COMMAND); + } + + public List readChoices() { + String input = readValidInputBy(Validator.CHOICE); + return Arrays.asList(input.split(CHOICE_DELIMITER)); + } + + public String readReMatchingCommand() { + return readValidInputBy(Validator.RE_MATCHING); + } + + private String readValidInputBy(Validator validator) { + String input = Console.readLine(); + validateBy(input, validator); + return input; + } + + private void validateBy(String input, Validator validator) { + validator.validate(input); + } +} diff --git a/src/main/java/pairmatching/view/OutputView.java b/src/main/java/pairmatching/view/OutputView.java new file mode 100644 index 000000000..9f0225b83 --- /dev/null +++ b/src/main/java/pairmatching/view/OutputView.java @@ -0,0 +1,49 @@ +package pairmatching.view; + +import pairmatching.domain.crew.Crew; +import pairmatching.view.message.Message; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +public class OutputView { + + public void printCommandGuide() { + print(Message.COMMAND_GUIDE.getMessage()); + } + + public void printChoiceGuide() { + print(Message.CHOICE_GUIDE.getMessage()); + } + + public void printMatchingResult(List> pairs) { + print(Message.MATCHING_RESULT_GUIDE.getMessage()); + for (Set pair : pairs) { + String result = makeMatchingResultMessage(pair); + print(result); + } + } + + private String makeMatchingResultMessage(Set pair) { + return pair.stream() + .map(Crew::getName) + .collect(Collectors.joining(Message.MATCHING_DELIMITER.getMessage())) + "\n"; + } + + public void printReMatchingGuide() { + print(Message.RE_MATCHING_GUIDE.getMessage()); + } + + public void printInitializingMessage() { + print(Message.INITIALIZING.getMessage()); + } + + public void printErrorMessage(String message) { + print(message); + } + + private void print(String message) { + System.out.print(message); + } +} diff --git a/src/main/java/pairmatching/view/message/Message.java b/src/main/java/pairmatching/view/message/Message.java new file mode 100644 index 000000000..e616effd9 --- /dev/null +++ b/src/main/java/pairmatching/view/message/Message.java @@ -0,0 +1,35 @@ +package pairmatching.view.message; + +public enum Message { + COMMAND_GUIDE("\n기능을 선택하세요.\n" + + "1. 페어 매칭\n" + + "2. 페어 조회\n" + + "3. 페어 초기화\n" + + "Q. 종료\n"), + CHOICE_GUIDE("\n#############################################\n" + + "과정: 백엔드 | 프론트엔드\n" + + "미션:\n" + + " - 레벨1: 자동차경주 | 로또 | 숫자야구게임\n" + + " - 레벨2: 장바구니 | 결제 | 지하철노선도\n" + + " - 레벨3: \n" + + " - 레벨4: 성능개선 | 배포\n" + + " - 레벨5: \n" + + "############################################\n" + + "과정, 레벨, 미션을 선택하세요.\n" + + "ex) 백엔드, 레벨1, 자동차경주\n"), + MATCHING_RESULT_GUIDE("\n페어 매칭 결과입니다.\n"), + MATCHING_DELIMITER(" : "), + RE_MATCHING_GUIDE("\n매칭 정보가 있습니다. 다시 매칭하시겠습니까?\n" + + "네 | 아니오\n"), + INITIALIZING("\n초기화 되었습니다.\n"); + + private final String message; + + Message(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/pairmatching/view/validator/Validator.java b/src/main/java/pairmatching/view/validator/Validator.java new file mode 100644 index 000000000..b9567e83e --- /dev/null +++ b/src/main/java/pairmatching/view/validator/Validator.java @@ -0,0 +1,33 @@ +package pairmatching.view.validator; + +public enum Validator { + COMMAND("\\w{1}", "[ERROR] 기능 선택은 한 개의 문자만 입력이 가능합니다.\n"), + CHOICE(".{3,5},.{4},.{3,7}", "[ERROR] 올바른 입력이 아닙니다. \", \"로 구분하여 과정, 레벨, 미션을 입력하십시오.\n"), + RE_MATCHING("([가-힣]{1}|[가-힣]{3})", "[ERROR] %s는 잘못된 입력입니다. 네, 아니오 중 한 가지를 입력하십시오.\n"); + + private final String validFormat; + private final String errorMessage; + + Validator(String validFormat, String errorMessage) { + this.validFormat = validFormat; + this.errorMessage = errorMessage; + } + + public String getValidFormat() { + return validFormat; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void validate(String input) { + if (isInvalidFormat(input)) { + throw new IllegalArgumentException(errorMessage); + } + } + + private boolean isInvalidFormat(String input) { + return !input.matches(validFormat); + } +}