-
Notifications
You must be signed in to change notification settings - Fork 252
[번외편 - 처음부터 다시하는 사다리 게임] 로빈(임수빈) 미션 제출합니다. #411
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
base: robinjoon
Are you sure you want to change the base?
Changes from all commits
cb4f68c
88a9e5c
032a69f
3dadff9
8b8e086
79544fa
a772d45
28c6612
9ddefca
5b6ab9e
b848e07
199ea26
a9e0ee7
4906acc
cbc96d5
1454024
9eb6999
1e4734e
10830e5
08cce9c
d040005
bb4f3db
b7da5b0
256705d
f8b5079
9003f6a
8c44c73
8b60a91
b0424e0
a0c4c77
2e2934b
54b99be
48a090f
4f90e64
b1ec852
fc96389
cbda7c3
81f9ddb
d39e189
7a94a68
f1e0c5d
2de2881
a63c480
451ada1
25e65b4
12f263b
bbe2d23
577bff0
87f6217
0cdcd70
ff6d865
fe2d5e7
2fc4743
edcbc69
bed688b
e819163
01a40df
8e843cf
060ad0c
fc41f53
9c1ae95
021dbb8
ffed2ff
b2cb299
6f0e811
4423b70
1710098
2e2989c
ad9233d
de3a4e2
9ad6d12
8adb21c
f183b9c
5898e16
a2d2785
1e16562
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,88 +1,59 @@ | ||
| # java-ladder | ||
|
|
||
| 사다리 타기 미션 저장소 | ||
|
|
||
| ## 우아한테크코스 코드리뷰 | ||
|
|
||
| - [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) | ||
|
|
||
| ## 기능 요구사항 해석 | ||
|
|
||
| 1. 사다리 게임에 참여하는 사람에 이름을 최대5글자까지 부여할 수 있다. 사다리를 출력할 때 사람 이름도 같이 출력한다. | ||
| - 사람의 이름은 중복이 되어선 안된다. | ||
| - 이름은 최소 1글자 최대 5글자 까지 부여되어야 한다. | ||
| - 알파뱃 대 소문자로 이루어 진다. | ||
| - 사람 이름은 "all"일 수 없다. | ||
|
|
||
| 2. 사람 이름은 쉼표(,)를 기준으로 구분한다. | ||
| - 쉼표로 시작하거나 쉼표로 끝내면 예외 발생 | ||
| - 쉼표로 나뉘는데, 알파벳 대소문자가 아닌 경우 예외 발생 | ||
| - 사람은 최대 10명까지 받을 수 있다. | ||
|
|
||
| 3. 사다리 높이를 입력할 수 있다. | ||
| - 사다리 높이는 5 이상 10 이하의 정수로 입력해야 한다. | ||
| - 사다리의 폭은 사람들의 수이다. | ||
|
|
||
| 4. 실행 결과를 입력할 수 있다. 실행 결과란, 사다리 게임에서 사다리 아래의 항목을 말한다. | ||
| - 실행 결과는 쉼표(,)를 기준으로 구분한다. | ||
| - 각 실행 결과는 최소 1글자 최대 5글자 까지 부여되어야 한다. | ||
| - 각 실행 결과는 공백 문자만으로 이루어질 수 없다. | ||
|
|
||
| 5. 사람 이름을 5자 기준으로 출력하기 때문에 사다리 폭도 넓어져야 한다. | ||
| - 사다리의 가로(`-`)는 `최대 이름 길이`로 고정한다. | ||
| - 사람의 이름은 사다리의 세로(`|`) 에 맞춰 정렬한다. | ||
| - 사람의 이름이 `최대 이름 길이`보다 작을경우 다음과 같은 규칙으로 공백을 추가한다. | ||
|
|
||
| ``` | ||
| `a` -> ` a ` | ||
| `aa` -> ` aa ` | ||
| `aaa` -> ` aaa ` | ||
| `aaaa` -> `aaaa ` | ||
| `aaaaa` -> `aaaaa` | ||
| ``` | ||
|
|
||
| 6. 사다리 타기가 정상적으로 동작하려면 라인이 겹치지 않도록 해야 한다. | ||
| - |-----|-----| 모양과 같이 가로 라인이 겹치는 경우 어느 방향으로 이동할지 결정할 수 없다. | ||
| 7. 사용자 입력 중 예외가 발생할 경우, 예외 발생 원인을 출력한 뒤, 처음부터 다시 입력받는다. | ||
| 8. 사다리 게임 실행 결과를 알 수 있다. | ||
| - all 을 입력한 경우 전체 결과를 알 수 있다. | ||
| - 앞서 입력한 사람 이름 중 하나를 입력하면 그 사람의 결과를 알 수 있다. | ||
| - 잘못된 입력을 할 경우, 예외 메세지를 출력한 뒤 결과를 보고 싶은 사람 이름부터 다시 입력받는다. | ||
|
|
||
| ## 기능 목록 | ||
|
|
||
| - [x] 사람 이름 입력 기능 | ||
| - [x] 사람 이름 길이 검증 기능 | ||
| - [x] 사람 이름 문자 검증 가능 | ||
| - [x] 사람 생성 기능 | ||
| - [x] 사람 이름 중복 검증 기능 | ||
| - [x] ","로 구분된 사람 이름 입력 기능 | ||
| - [x] 사람 이름 개수 검증 기능 | ||
| # 요구사항 해석 | ||
|
|
||
| > 다시 구현하는 것인 만큼, 조금 난이도를 높이기 위해 생성되는 사다리에 제약 사항을 추가했습니다. | ||
|
|
||
| - 참가자 관련 요구 사항 | ||
| 1. 참가자는 최대 10명까지 입력 가능하다 | ||
| 2. 참가자 이름의 구분은 ","를 기준으로 수행된다. | ||
| - 참가자 이름 관련 요구 사항 | ||
| 1. 참가자 이름은 최소 한글자 최대 5글자까지 부여할 수 있다. | ||
| 2. 참가자 이름은 중복될 수 없다. | ||
| 3. 참가자 이름에는 알파벳 대소문자, 숫자 만 입력할 수 있다. | ||
| 4. 참가자 이름은 all이 될 수 없다. | ||
| - 사다리 관련 요구 사항 | ||
| 1. 어느 한 위치에서 왼쪽과 오른쪽에 동시에 발판이 있을 수 없다. | ||
| - 즉, 다음과 같은 사다리는 생성되면 안된다. | ||
| ``` | ||
| |-----| | | ||
| |-----|-----| | ||
| ``` | ||
| 2. 사다리의 높이는 최소 2, 최대 20이다. | ||
| 3. 두 참여자 사이에는 최소한 한개 이상의 발판이 있어야 한다. | ||
| - 즉, 다음과 같은 사다리는 생성되면 안된다. | ||
| ``` | ||
| |-----| | | ||
| |-----| | | ||
| |-----| | | ||
| ``` | ||
| - 상품 관련 요구 사항 | ||
| 1. 상품은 사다리 타기의 결과로 참여자와 1대 1 매칭 될 어떤 것을 말한다. | ||
| 2. 상품은 최소 한글자 최대 5글자까지 부여할 수 있다. | ||
| - 안정적인 프로그램 실행을 위한 요구사항 | ||
| 1. 잘못된 입력이 발생한 경우, 적절한 에러 메세지를 출력한 뒤 해당 위치부터 다시 입력을 수행한다. | ||
| 2. 재입력은 무한히 시도될 수 있어야 한다. | ||
| - 출력 요구 사항 | ||
| 1. 사다리의 가로(-)는 최대 이름 길이로 고정한다. | ||
| 2. 사람의 이름은 사다리의 세로(|) 에 맞춰 정렬한다. | ||
| 3. 사람의 이름이 최대 이름 길이보다 작을경우 다음과 같은 규칙으로 공백을 추가한다. | ||
| 1. 이름 맨 뒤에 공백 문자를 추가한다. | ||
| 2. 앞에서 추가된 공백 문자를 포함한 이름의 길이가 최대 이름 길이 보다 작을 경우, 공백을 포함한 이름의 길이가 최대 이름 길이가 되도록 공백 문자를 이름 앞에 추가한다. | ||
|
|
||
| # 기능 목록 | ||
|
|
||
| - [x] 참가자 이름 입력 기능 | ||
| - [x] 참가자 생성 기능 | ||
| - [x] 사다리 높이 입력 기능 | ||
| - [x] 사다리 높이 검증 기능 | ||
| - [x] 상품 이름 입력 기능 | ||
| - [x] 상품 생성 기능 | ||
| - [x] 사다리 생성 기능 | ||
| - [x] 사다리 높이 지정 기능 | ||
| - [x] 사다리 전체 폭 지정 기능 | ||
| - [x] 세로 라인 생성 기능 | ||
| - [x] 가로 라인 생성 기능 | ||
| - [x] 사람 이름, 사다리 출력 기능 | ||
| - [x] 사람 이름 출력 기능 | ||
| - [x] 사다리 출력 기능 | ||
| - [x] 입력 예외 처리 기능 | ||
| - [x] 실행 결과 입력 기능 | ||
| - [x] 실행 결과 판독 기능 | ||
| - [x] 실행 결과를 보고 싶은 이름 입력 기능 | ||
| - [x] 실행 결과 출력 기능 | ||
|
|
||
| ## 1단계 피드백 반영 사항 | ||
|
|
||
| - [x] Pattern 객체 재사용하도록 수정 | ||
| - [x] 도메인 로직에서 UI 로직 분리 | ||
| - [x] ~ String 클래스 이름 변경 | ||
| - [x] ~ String 클래스 view 패키지로 이동 | ||
| - [x] 이름 관련 정책 구현 위치 view로 이동 | ||
| - [x] 사다리 출력 기능 | ||
| - [x] 사다리 타기 실행 기능 | ||
| - [x] 사다리 타기 결과 출력 기능 | ||
|
|
||
| ## 2단계 피드백 반영 사항 | ||
| # 피드백 반영 예정 목록 | ||
|
|
||
| - [x] 모호한 변수 및 메서드 명 변경 | ||
| - [x] 적절하지 않은 예외 사용 코드 삭제 | ||
| - [x] List와 가변 배열 사이의 불필요한 변환 작업 삭제 | ||
| - [x] Ladder 의 정적 팩토리 메서드 추가 | ||
| - [x] 표준 함수형 인터페이스 대신 적절한 커스텀 인터페이스 정의 | ||
| - [x] 불필요한 매개변수 삭제 | ||
| - [x] 생성자와 정적 팩토리 메서드에 매개변수 차이 부여 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,99 +1,86 @@ | ||
| import domain.LadderGame; | ||
| import domain.LadderGame.LadderGameBuilder; | ||
| import domain.LineGenerateStrategy; | ||
| import domain.RandomLineGenerateStrategy; | ||
| import dto.LadderGameResults; | ||
| import java.util.List; | ||
| import java.util.function.Supplier; | ||
| import view.ClimbResultPrinter; | ||
| import util.RetryHelper; | ||
| import view.GiftsInputView; | ||
| import view.InputView; | ||
| import view.LadderGameOperatorInputView; | ||
| import view.LadderPrinter; | ||
| import view.NameInputView; | ||
| import view.NamesPrinter; | ||
| import view.LadderHeightInputView; | ||
| import view.OutputView; | ||
| import view.ResultInputView; | ||
| import view.PlayersInputView; | ||
|
|
||
| public class Main { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
view(프론트엔드) 에서는 보통 값의 타입이나 형태(정규식) 정도를 검증해줄 텐데요 저는 프론트와 약속하기 나름이라고 생각해요
재사용한다면 domain, view 어느 방향으로든 의존이 생길텐데 저는 지양하고 싶군요! 현재는 간단한 Console Application 이고 |
||
| public static void main(String[] args) { | ||
| LadderGame ladderGame = generateLadderGame(); | ||
| RetryHelper retryHelper = new RetryHelper(10); | ||
| List<String> playerNames = getPlayerNames(retryHelper); | ||
| List<String> giftNames = getGiftNames(retryHelper, playerNames); | ||
| Integer ladderHeight = getLadderHeight(retryHelper); | ||
|
|
||
| printName(ladderGame); | ||
| printLadder(ladderGame); | ||
| printResults(ladderGame); | ||
| LadderGame ladderGame = makeLadderGame(playerNames, giftNames, ladderHeight); | ||
| printLadderGame(playerNames, giftNames, ladderGame); | ||
|
|
||
| List<String> rawNames = ladderGame.getRawNames(); | ||
| printClimbResult(ladderGame, rawNames); | ||
| printLadderGameResults(playerNames, ladderGame); | ||
| } | ||
|
|
||
| private static LadderGame generateLadderGame() { | ||
| return RetryHelper.retry(() -> { | ||
| private static List<String> getPlayerNames(RetryHelper retryHelper) { | ||
| return retryHelper.retry(() -> { | ||
| OutputView.print("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); | ||
| List<String> names = NameInputView.getNames(InputView::getInput); | ||
| OutputView.print("실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)"); | ||
| List<String> rawResults = ResultInputView.getResults(InputView::getInput, names.size()); | ||
| OutputView.print("최대 사다리 높이는 몇 개인가요?"); | ||
| int ladderHeight = Integer.parseInt(InputView.getInput()); | ||
| return new LadderGame(names, ladderHeight, rawResults); | ||
| return PlayersInputView.getPlayerNames(InputView.getInput()); | ||
| }); | ||
| } | ||
|
|
||
| private static void printName(LadderGame ladderGame) { | ||
| OutputView.print(NamesPrinter.from(ladderGame.getRawNames())); | ||
| private static List<String> getGiftNames(RetryHelper retryHelper, List<String> playerNames) { | ||
| return retryHelper.retry(() -> { | ||
| OutputView.print("실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)"); | ||
| return GiftsInputView.getGiftNames(InputView.getInput(), playerNames.size()); | ||
| }); | ||
| } | ||
|
|
||
| private static void printLadder(LadderGame ladderGame) { | ||
| OutputView.print(LadderPrinter.from(ladderGame.getRawLadder())); | ||
| private static Integer getLadderHeight(RetryHelper retryHelper) { | ||
| return retryHelper.retry(() -> { | ||
| OutputView.print("최대 사다리 높이는 몇 개인가요?"); | ||
| return LadderHeightInputView.getLadderHeight(InputView.getInput()); | ||
| }); | ||
| } | ||
|
|
||
| private static void printResults(LadderGame ladderGame) { | ||
| OutputView.print(NamesPrinter.from(ladderGame.getRawResults())); | ||
| private static LadderGame makeLadderGame(List<String> playerNames, List<String> giftNames, Integer ladderHeight) { | ||
| LineGenerateStrategy lineGenerateStrategy = new RandomLineGenerateStrategy(); | ||
| return makeLadderGame(playerNames, giftNames, ladderHeight, lineGenerateStrategy); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 위에 return makeLadderGame(playerNames, giftNames, ladderHeight, new RandomLineGenerateStrategy()); |
||
| } | ||
|
|
||
| private static void printClimbResult(LadderGame ladderGame, List<String> rawNames) { | ||
| String gameOperator = getGameOperator(ladderGame); | ||
|
|
||
| gameOperator = printClimbResultsUntilOperatorIsAll(ladderGame, gameOperator); | ||
|
|
||
| List<String> climbResults = ClimbResultPrinter.of(rawNames, ladderGame.getClimbResults(gameOperator)); | ||
| climbResults.forEach(OutputView::print); | ||
| private static LadderGame makeLadderGame(List<String> playerNames, List<String> giftNames, Integer ladderHeight, | ||
| LineGenerateStrategy randomLineMakeStrategy) { | ||
| return LadderGameBuilder.builder() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LadderGameBuilder 가 외부로 노출되었는데
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
무슨 말인지 잘 모르겠습니다... 빌더는 외부로 노출되어야 하는 것이 아닌가요??? |
||
| .players(playerNames) | ||
| .gifts(giftNames) | ||
| .ladderHeight(ladderHeight) | ||
| .lineGenerateStrategy(randomLineMakeStrategy) | ||
| .build(); | ||
| } | ||
|
|
||
| private static String getGameOperator(LadderGame ladderGame) { | ||
| OutputView.print("결과를 보고 싶은 사람은?"); | ||
| String gameOperator = RetryHelper.retry( | ||
| () -> LadderGameOperatorInputView.getOperator(InputView::getInput, ladderGame.getRawNames()) | ||
| ); | ||
| OutputView.print("실행 결과"); | ||
| return gameOperator; | ||
| private static void printLadderGame(List<String> playerNames, List<String> giftNames, LadderGame ladderGame) { | ||
| OutputView.printPlayers(playerNames); | ||
| OutputView.printLadder(ladderGame.rawLadder()); | ||
| OutputView.printGifts(giftNames); | ||
| } | ||
|
|
||
| private static String printClimbResultsUntilOperatorIsAll(LadderGame ladderGame, String gameOperator) { | ||
| while (!gameOperator.equals("all")) { | ||
| List<String> climbResults = ClimbResultPrinter.of( | ||
| List.of(gameOperator), | ||
| ladderGame.getClimbResults(gameOperator) | ||
| ); | ||
| climbResults.forEach(OutputView::print); | ||
|
|
||
| gameOperator = getGameOperator(ladderGame); | ||
| private static void printLadderGameResults(List<String> playerNames, LadderGame ladderGame) { | ||
| String ladderGameResultOwner = showLadderGameResult(playerNames, ladderGame); | ||
| while (!ladderGameResultOwner.equals("all")) { | ||
| ladderGameResultOwner = showLadderGameResult(playerNames, ladderGame); | ||
| } | ||
| return gameOperator; | ||
| } | ||
|
|
||
| static final class RetryHelper { | ||
|
|
||
| public static <E> E retry(Supplier<E> supplier) { | ||
| E result = null; | ||
| while (result == null) { | ||
| result = useSupplier(supplier); | ||
| } | ||
| return result; | ||
| } | ||
|
|
||
| private static <E> E useSupplier(Supplier<E> supplier) { | ||
| try { | ||
| return supplier.get(); | ||
| } catch (Exception e) { | ||
| System.out.println(e.getMessage()); | ||
| return null; | ||
| } | ||
| } | ||
| private static String showLadderGameResult(List<String> playerNames, LadderGame ladderGame) { | ||
| RetryHelper retryHelper = new RetryHelper(10); | ||
| return retryHelper.retry(() -> { | ||
| String operator = LadderGameOperatorInputView.getOperator(InputView.getInput(), playerNames); | ||
| LadderGameResults ladderGameResults = ladderGame.start(operator); | ||
| OutputView.printLadderGameResults(ladderGameResults); | ||
| return operator; | ||
| }); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| abstract non-sealed class AbstractLineGenerateStrategy implements LineGenerateStrategy { | ||
| @Override | ||
| public final List<Boolean> generate(int lineSize) { | ||
| List<Boolean> generate = new ArrayList<>(generateStrategy(lineSize)); | ||
| fixInvalidBridges(generate); | ||
| return Collections.unmodifiableList(generate); | ||
| } | ||
|
|
||
| public abstract List<Boolean> generateStrategy(int lineSize); | ||
|
|
||
| private void fixInvalidBridges(List<Boolean> rawBridges) { | ||
| rawBridges.set(rawBridges.size() - 1, false); | ||
| for (int index = 1; index < rawBridges.size() - 1; index++) { | ||
| fixIfNeed(rawBridges, index); | ||
| } | ||
| } | ||
|
|
||
| private void fixIfNeed(List<Boolean> rawBridges, int index) { | ||
| if (isBridgeInARow(rawBridges, index)) { | ||
| rawBridges.set(index, false); | ||
| } | ||
| } | ||
|
|
||
| private boolean isBridgeInARow(List<Boolean> rawBridges, int index) { | ||
| return rawBridges.get(index) && rawBridges.get(index - 1); | ||
| } | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package domain; | ||
|
|
||
| import java.util.function.Function; | ||
|
|
||
| enum Direction { | ||
|
|
||
| RIGHT(index -> index + 1), LEFT(index -> index - 1), STRAIGHT(index -> index); | ||
| private final Function<Integer, Integer> nextIndexFunction; | ||
|
|
||
| Direction(Function<Integer, Integer> nextIndexFunction) { | ||
| this.nextIndexFunction = nextIndexFunction; | ||
| } | ||
|
|
||
| Point nextPoint(int nowIndex) { | ||
| Integer nextIndex = nextIndexFunction.apply(nowIndex); | ||
| if (this == LEFT) { | ||
| return new Point(RIGHT, nextIndex); | ||
| } | ||
| if (this == RIGHT) { | ||
| return new Point(LEFT, nextIndex); | ||
| } | ||
| return new Point(STRAIGHT, nextIndex); | ||
| } | ||
| } |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이번에 추가하신 요구사항 맞을까요? 😃
도전하는것 너무 좋네요 💯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 뭔가 그대로 하는 것 보단, 뭐라도 좀 어렵게 해보는게 좋을 것 같아서 추가해봤습니다!