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

[2단계 - 사다리 타기] 이든(최승준) 미션 제출합니다. #366

Merged
merged 40 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
65cc025
refactor : 클래스명이 의미를 더 잘 표현하도록 수정
PgmJun Feb 25, 2024
7226a19
refactor : 오라클 자바 컨벤션에 맞도록 클래스 멤버 위치 조정
PgmJun Feb 25, 2024
d8cbe46
refactor : 중복되는 네이밍 변경
PgmJun Feb 25, 2024
9ab2f29
refactor : BridgeGeneratorStub 클래스, NPE 문제 방지
PgmJun Feb 26, 2024
8cc519d
remove : 입력 테스트 제거
PgmJun Feb 26, 2024
b46f1d5
docs : Step2 기능 구현 문서 추가
PgmJun Feb 26, 2024
94292ee
feat : 사다리 결과 출력 기능 구현
PgmJun Feb 26, 2024
4c13691
refactor : 참가자 이름 입력 시, 구분자가 가장 마지막에 오는 경우 검증하도록 수정
PgmJun Feb 26, 2024
d52cf71
refactor : 패키지 구조 변경
PgmJun Feb 26, 2024
55d9d3d
feat : 사다리 실행 결과 확인 기능 구현
PgmJun Feb 27, 2024
585db12
refactor : CustomException 제거
PgmJun Feb 27, 2024
fef4282
refactor : 예외 메시지는 발생시키는 곳에서 관리하도록 변경
PgmJun Feb 27, 2024
c021e37
refactor : 상수 네이밍 깔끔하게 변경
PgmJun Feb 27, 2024
fa2fd59
refactor : 중복되는 bridge 랜덤 생성 로직 메서드 분리
PgmJun Feb 27, 2024
0f8e40f
remove : 의미없는 상수 제거
PgmJun Feb 27, 2024
e44fd39
feat : 사용할 수 없는 이름 추가
PgmJun Feb 27, 2024
027deda
refactor : 재입력 메서드 네이밍 동일하게 변경(오버로딩)
PgmJun Feb 27, 2024
62b6580
refactor : 커맨드 관련 상수 열거 클래스로 분리
PgmJun Feb 27, 2024
a61097e
refactor : 필드명으로 value 사용
PgmJun Feb 27, 2024
c22e650
refactor : 불필요한 확장성을 위해 만든 RetryableController 삭제
PgmJun Feb 27, 2024
6e8f2cf
refactor : InputView에서만 사용되는 상수에 private 접근 제어 설정
PgmJun Feb 27, 2024
c38d1bd
refactor : 코드라인 정리
PgmJun Feb 27, 2024
3dfe45e
refactor : 코드 컨벤션에 맞춰 클래스 멤버 위치 조정
PgmJun Feb 27, 2024
17f859e
refactor : ui 로직에 의존하던 LadderBridge enum 코드 제거
PgmJun Feb 28, 2024
d44bbbd
refactor : Players 사다리 결과 형식 상수화
PgmJun Feb 28, 2024
bca81d8
docs : 사다리 종료 커멘드 관련 문서 수정
PgmJun Feb 28, 2024
9543acc
remove : LadderBridge getter 삭제
PgmJun Feb 28, 2024
65a578c
refactor : 오라클 컨벤션에 맞게 멤버 위치 변경
PgmJun Feb 28, 2024
ea3e039
refactor : 테스트 클래스 위치, 네이밍 수정
PgmJun Feb 28, 2024
04f71c7
refactor : 테스트 내부 클래스로 분리
PgmJun Feb 28, 2024
48c6982
refactor : 중복 로직 메서드 분리
PgmJun Feb 28, 2024
5c2a355
refactor : final 키워드 사용해서 재할당 불가능하게 수정
PgmJun Feb 28, 2024
dc65b6e
refactor : final 키워드 사용해서 재할당 불가능하게 수정
PgmJun Feb 28, 2024
7b9f32b
refactor : 직관적인 에러 메시지 네이밍 사용
PgmJun Feb 29, 2024
4359d9d
refactor : ealry return 적용하여 else 키워드 제거
PgmJun Feb 29, 2024
120d838
refactor : Ladder의 책임 분리
PgmJun Feb 29, 2024
f96ca82
refactor : 한번에 index, name 조회해오도록 변경
PgmJun Feb 29, 2024
9b281d0
refactor : 클래스 변수, 인스턴스 변수 구분
PgmJun Feb 29, 2024
1ba0367
refactor : n번 조회 방식에서 단일 조회 방식으로 변경
PgmJun Feb 29, 2024
9670383
refactor : OS 별로 다른 이스케이프 문자 고려하여 코드 수정
PgmJun Feb 29, 2024
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
60 changes: 58 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
## 기능 구현 사항
- [x] 참가자 이름 입력 기능
- [x] 콤마(,)로 구분해서 입력해야한다.
- [x] 이름에 공백이 붙어있으면 trim으로 공백을 제거한다.
- [x] 이름에 공백이 붙어있으면 replace()로 공백을 제거한다.
- [x] ⚠️ 이름 길이는 1 이상, 5 이하가 아니면 예외가 발생한다.
- [x] ⚠️ 공백 문자 입력 시, 예외가 발생한다.
- [ ] ⚠️ `all`,`fin` 이라는 이름 사용 시, 예외가 발생한다.

- [x] 실행 결과 입력 기능
- [x] 콤마(,)로 구분해서 입력해야한다.
- [x] 이름에 공백이 붙어있으면 replace()로 공백을 제거한다.
- [x] ⚠️ 길이가 1 이상, 5 이하가 아니면 예외가 발생한다.
- [x] ⚠️ 공백 문자 입력 시, 예외가 발생한다.
- [x] ⚠️ 참가자 이름의 수와 실행결과의 수가 다르면, 예외가 발생한다.

- [x] 사다리 높이 입력 기능
- [x] ⚠️ 정수형태가 아니면 예외가 발생한다.
Expand All @@ -12,17 +20,65 @@
- [x] 입력한 높이의 사다리 생성
- [x] |-----|-----| 모양과 같이 가로 라인이 겹치지 않도록 생성한다.

- [x] 결과 출력 기능
- [x] 사다리 실행 결과 출력 기능
- [x] 실행결과 메시지 출력
- [x] 참가자 이름 출력
- [x] 참가자 이름을 5자로 정의하고, 빈 칸이 생기면 공백으로 5자가 되도록 채운다.
- [x] 생성된 사다리를 출력
- [x] 실행 결과 출력

- [x] 사다리 실행 결과 확인 기능
- [x] 이름 입력
- [x] 단일 이름 입력 시, 단일 참가자의 결과 출력
- [x] `all` 입력 시, 전체 참가자의 결과 출력
- [x] ⚠️ 존재하지 않는 참가자 이름을 입력 시, 예외가 발생한다.
- [x] 해당하는 이름의 사다리 실행 결과 출력
- [x] `fin` 입력 시, 게임 종료

## 실행 결과
```
참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)
pobi,honux,crong,jk

실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)
꽝,5000,꽝,3000

최대 사다리 높이는 몇 개인가요?
5

사다리 결과

pobi honux crong jk
|-----| |-----|
| |-----| |
|-----| | |
| |-----| |
|-----| |-----|
꽝 5000 꽝 3000

결과를 보고 싶은 사람은?
pobi

실행 결과

결과를 보고 싶은 사람은?
all

실행 결과
pobi : 꽝
honux : 3000
crong : 꽝
jk : 5000
```


## 추가 구현 사항
- [x] Exception 발생 시, 오류 메시지 출력하고 해당 부분부터 재입력
- [x] 재입력 횟수가 10회 초과되면 예외가 발생되고 프로그램이 종료된다.
- [x] 참가자 인원은 2 이상, 10 이하여야한다.
- [x] 참가자 이름은 중복되지 않아야한다.
- [x] 사다리 실행 결과 입력 시, `fin`을 입력하면 게임이 종료된다.

## 프로그래밍 요구사항
- [x] indent(인덴트, 들여쓰기) depth를 2를 넘지 않도록 구현한다. 1까지만 허용한다.
Expand Down
16 changes: 11 additions & 5 deletions src/main/java/LadderGameApplication.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import controller.LadderController;
import domain.Ladder;
import domain.ladder.Ladder;
import domain.ladder.LadderHeight;
import domain.ladder.LadderResults;
import domain.player.PlayerNames;
import view.InputView;
import view.OutputView;

Expand All @@ -8,10 +11,13 @@
public class LadderGameApplication {

public static void main(String[] args) {
InputView inputView = new InputView(new Scanner(System.in));
OutputView outputView = new OutputView();
LadderController ladderController = new LadderController(inputView, outputView);
LadderController ladderController = new LadderController(new InputView(new Scanner(System.in)), new OutputView());

Ladder ladder = ladderController.createLadder();
PlayerNames playerNames = ladderController.readPlayerNames();
LadderResults results = ladderController.readLadderResults(playerNames.getCount());
LadderHeight ladderHeight = ladderController.readLadderHeight();

Ladder ladder = ladderController.createLadder(ladderHeight, playerNames, results);
ladderController.matchPlayerToResult(ladder, playerNames, results);
}
}
8 changes: 0 additions & 8 deletions src/main/java/common/exception/CustomException.java

This file was deleted.

36 changes: 0 additions & 36 deletions src/main/java/common/exception/message/ExceptionMessage.java

This file was deleted.

5 changes: 1 addition & 4 deletions src/main/java/common/exception/model/IOException.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package common.exception.model;

import common.exception.CustomException;

public class IOException extends CustomException {

public class IOException extends RuntimeException {
public IOException(String message) {
super(message);
}
Expand Down
5 changes: 1 addition & 4 deletions src/main/java/common/exception/model/NotFoundException.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package common.exception.model;

import common.exception.CustomException;

public class NotFoundException extends CustomException {

public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package common.exception.model;

import common.exception.CustomException;

public class ValidationException extends CustomException {

public class ValidationException extends RuntimeException {
public ValidationException(String message) {
super(message);
}
Expand Down
40 changes: 0 additions & 40 deletions src/main/java/controller/Controller.java

This file was deleted.

95 changes: 80 additions & 15 deletions src/main/java/controller/LadderController.java
Original file line number Diff line number Diff line change
@@ -1,45 +1,110 @@
package controller;

import domain.Ladder;
import domain.LadderHeight;
import domain.PlayerName;
import domain.PlayerNames;
import common.exception.model.IOException;
import domain.bridge.strategy.RandomBridgeGenerator;
import domain.ladder.Ladder;
import domain.ladder.LadderHeight;
import domain.ladder.LadderResults;
import domain.player.PlayerName;
import domain.player.PlayerNames;
import view.InputView;
import view.OutputView;
import view.command.Command;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;

public class LadderController extends Controller {
public class LadderController {
public static final int READ_LIMIT = 10;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제이는 상수화를 자주 사용하시는 지, 그리고 어떤 기준으로 상수화를 사용하시는 지 궁금합니다.

저는 값 자체에서 의미가 드러나지 않는 경우에 상수화를 사용하는 편입니다.
예를 들어 "%s : %s" 이러한 포맷 관련 문자열이나, 1, 2와 같이 덩그러니 있을 때 어떤 의미인 지 알 수 없는 값이 존재하는 경우
상수화를 통해 값에 이름을 붙여주어 조금 더 명확하고 가독성 좋은 코드를 만들려고 합니다.

이런 생각에 대해 어떻게 생각하시는 지와 제이의 의견을 듣고 싶습니다!

의미가 드러나지 않는 경우에 사용한다는거에 공감을 하고 더 나아가서 유지보수측면에서도 좋다고생각합니다. 예를 들어, 한 군데에서만 사용되고있는 숫자가 로직이 발전되면서 다른곳에서도 같은 숫자를 쓰게되는경우가 있을거에요. 요구사항 변경으로 해당 숫자를 변경해야할때 모두 찾아서 수정해주어야합니다. 실수로 한군데라도 빠트리게되면 버그가 생기겠죠.
그리고 문서화측면(self-documenting)에서도 좋습니다. 상수만보고 코드가 어떤 일들을 하는지 알 수 있습니다. 마지막으로 보안측면도있는데요, 값을 변경할 수 없으므로 런타임때 값이 의도치않게 바뀌는걸 막아줍니다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

또한 만들어진 상수를 외부에서 관리한다 라는 의견과 사용하는 곳에서 관리한다 라는 의견이 있는데
저는 사용하는 곳에서 관리해야 유지보수성과 가독성이 훨씬 증가한다고 생각합니다.
이 부분에 대해서도 의견이 궁금해요!

상황에 따라 다르다고생각해요. 1차적으로는 저도 사용하는곳에서 관리를 해야 관리가 편하다고생각하는데요, 다른곳에서도 그 상수를 참조할경우에는 공통으로 만드는게 낫겟죠. 다만 필요해지는 순간에 공통으로 정의합니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

필요해지는 순간에 공통으로 라는 말에 공감합니다! 좋은 의견 감사합니다.

public static final String READ_LIMIT_OVER = String.format("입력 횟수 제한(%d)를 초과하였습니다", READ_LIMIT);

private final InputView inputView;
private final OutputView outputView;
private int retryCount;

public LadderController(InputView inputView, OutputView outputView) {
super(inputView, outputView);
this.inputView = inputView;
this.outputView = outputView;
this.retryCount = 0;
}

public Ladder createLadder() {
PlayerNames playerNames = readPlayerNames();
LadderHeight ladderHeight = readLadderHeight();

Ladder ladder = Ladder.create(ladderHeight, playerNames, new RandomBridgeGenerator());
outputView.printLadder(playerNames, ladder);
public Ladder createLadder(final LadderHeight ladderHeight, final PlayerNames playerNames, final LadderResults results) {
Ladder ladder = Ladder.create(ladderHeight, playerNames.getCount(), new RandomBridgeGenerator());
outputView.printGeneratedLadder(ladder, playerNames, results);

return ladder;
}

private PlayerNames readPlayerNames() {
public PlayerNames readPlayerNames() {
return retry(() -> createPlayerNames(inputView.readPlayerNames()));
}

public PlayerNames createPlayerNames(String[] playerNamesInput) {
private PlayerNames createPlayerNames(final String[] playerNamesInput) {
List<PlayerName> playerNames = Arrays.stream(playerNamesInput)
.map(PlayerName::new)
.toList();

return new PlayerNames(playerNames);
}

private LadderHeight readLadderHeight() {
public LadderHeight readLadderHeight() {
return retry(() -> new LadderHeight(inputView.readLadderHeight()));
}

public LadderResults readLadderResults(final int playerCount) {
return retry(() -> new LadderResults(inputView.readLadderResults(), playerCount));
}

public void matchPlayerToResult(final Ladder ladder, final PlayerNames playerNames, final LadderResults results) {
retry(() -> findPlayerResult(ladder, playerNames, results));
outputView.printEndMessage();
}

private void findPlayerResult(final Ladder ladder, final PlayerNames playerNames, final LadderResults results) {
String inputPlayerName = inputView.readPlayerNameForGetResult();

while (!Command.isFinishCommand(inputPlayerName)) {
outputView.printPlayerLadderResult(getPlayerLadderResult(inputPlayerName, ladder, playerNames, results));
inputPlayerName = inputView.readPlayerNameForGetResult();
}
}

private Map<String, String> getPlayerLadderResult(final String inputPlayerName, final Ladder ladder,
final PlayerNames playerNames, final LadderResults results) {
if (Command.isAllCommand(inputPlayerName)) {
return ladder.findAllPlayersLadderResultValue(playerNames, results);
}
return ladder.findSinglePlayerLadderResultValue(inputPlayerName, playerNames, results);
}

<R> R retry(final Supplier<R> supplier) {
validateRetryCountLimit();
try {
R value = supplier.get();
retryCount = 0;
return value;
} catch (Exception exception) {
outputView.printErrorMessage(exception.getMessage());
return retry(supplier);
}
}

void retry(final Runnable runnable) {
validateRetryCountLimit();
try {
runnable.run();
retryCount = 0;
} catch (Exception exception) {
outputView.printErrorMessage(exception.getMessage());
retry(runnable);
}
}

private void validateRetryCountLimit() {
if (retryCount++ == READ_LIMIT) {
throw new IOException(READ_LIMIT_OVER);
}
}
}
34 changes: 0 additions & 34 deletions src/main/java/domain/Floor.java

This file was deleted.

Loading