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

[사다리 타기] 최인호 미션 제출합니다. #7

Open
wants to merge 4 commits into
base: inhooo00
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 50 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 구현할 기능 목록

## 입력
- [x] 사용자 이름 입력
- [ ] 쉼표로 구분
- [ ] 최대 5글자까지 가능
- [x] 사다리 높이 입력
- [ ] 자연수


## 출력
- [x] 실행 결과
- [ ] 첫번째 줄 : 사람 이름
- [ ] 그 아래 줄 : 사다리 그리기

## 기능
- [ ] 사람의 이름을 받고 , 기준으로 나누기.
- [ ] 한 줄에 | 개수를 사람 이름 개수만큼 반환.
- [ ] 랜덤 값 기능 구현
- [ ] 랜덤에 충족하면 ----- 그리기.
- [ ] 한줄의 |에 ----- 연결선이 동시에 나오지 않게
- [ ] 테스트 코드 작성


## TDD 객체 명세서
- [x] Height
- [x] 객체 생성 가능
- [ ] Ladder
- [x] 객체 생성 가능
- [ ] 전체적인 값이 맞는지 확인
- [ ] Line
- [x] 객체 생성 가능
- [ ] 예상되는 사다리 라인 문자열
- [x] Player
- [x] 객체 생성 가능
- [x] Players
- [x] 객체 생성 가능
- [x] util
- [x] LadderHeightValidator
- [x] 정상_입력
- [x] 정수가_아닌_입력
- [x] 음수_입력
- [x] 공백_입력
- [x] PlayerNamesValidator
- [x] 정상_입력
- [x] 공백_포함
- [x] 공백
- [x] 이름_길이_초과
- [x] 중복_입력
# 1차 리뷰 후 개선 방향
10 changes: 10 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import controller.LadderGameController;

import java.io.IOException;

public class Application {
public static void main(String[] args) throws IOException {
LadderGameController ladderGameController = new LadderGameController();
ladderGameController.run();
}
}
57 changes: 57 additions & 0 deletions src/main/java/controller/LadderGameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package controller;

import model.Height;
import model.Ladder;
import model.Player;
import model.Players;
import util.RandomFootholdGenerator;
import view.InputView;
import view.OutputView;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class LadderGameController {
private final InputView inputView;
private final OutputView outputView;

public LadderGameController() {
this.inputView = new InputView();
this.outputView = new OutputView();
}
Comment on lines +20 to +23

Choose a reason for hiding this comment

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

내부에서 의존성을 생성하는 것과,
외부로부터 의존성을 주입받는 것의 차이점에 대해 알아보면 좋을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

제가 알고 있는 건 외부에서 의존성 주입은

  1. 외부로부터 다양한 값들을 가져올 수 있다는 점. (업케스팅? 느낌)
  2. 결합도를 낮춰서, 위 기준으로는 LadderGameController 안에 코드 수정이 일어나지 않게 하기 위함.
    정도로 알고 있습니다.

위 코드 기준으로는 할당받을 inputView와 outputView가 다양한 값들이 들어올 일이 없어서 직접 생성하는 방법을 선택했는데 혹시 외부에서 주입받는 방법을 사용하는 것이 좋을까요??


public void run() throws IOException {
Players players = createPlayersFromInput();
List<String> playerNames = extractPlayerNames(players);
Height height = createHeightFromInput();
Ladder ladder = createLadder(players, height);

outputView.printResultSentence(ladder.getLines(), playerNames);
}

private Players createPlayersFromInput() throws IOException {
String[] playerNamesArray = inputView.readPlayerNames().split(",");

return new Players(Arrays.asList(playerNamesArray));
}

private List<String> extractPlayerNames(Players players) {
return players.getPlayers().stream()
.map(Player::getName)
.collect(Collectors.toList());
}

private Height createHeightFromInput() throws IOException {
return new Height(inputView.readLadderHeightNumber());
}

private Ladder createLadder(Players players, Height height) {
Ladder ladder = new Ladder();

ladder.makeLines(height, players);

return ladder;
Comment on lines +34 to +55

Choose a reason for hiding this comment

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

각각의 메소드가 하나의 역할만 수행하고 있네요 👍🏻

}
}
22 changes: 22 additions & 0 deletions src/main/java/exception/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package exception;

public enum ErrorMessage {

DUPLICATED_CAR_NAME("참여자 이름이 중복될 수 없습니다."),
NOT_ALLOW_BLANK("참여자 이름에 공백이 들어갈 수 없습니다."),
NOT_ALLOW_EMPTY("참여자 입력 값이 비어있을수 없습니다."),
INVALID_NAME_LENGTH("참여자의 이름은 5자 이하여야합니다."),
NOT_NUMERIC("시도 횟수 입력은 숫자여야 합니다."),
ONLY_NATURAL_NUMBER("시도 횟수는 자연수여야합니다"),
NOT_ALLOW_ZERO("사다리 높이는 0이 될 수 없습니다");

private final String message;

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

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

public class LadderGameValidationException {

Choose a reason for hiding this comment

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

각각 예외클래스 이름을 잘 지어주셨네요! 👍🏻

크게 리팩토링 해야할 부분은 아닌 것 같지만
만약에 발생할 수 있는 예외가 달라진다면 상속하는 부분을 일일이 바꿔줘야 할 것 같아요!
(예시로 IllegalArgumentException에서 그냥 RuntimeException으로 변경하라고 한다면?!)

이럴 경우에 어떤 방법이 있을지 고민해보면 좋을 것 같아요!


public static class DuplicatedPlayerNameException extends IllegalArgumentException {
public DuplicatedPlayerNameException() {
super(ErrorMessage.DUPLICATED_CAR_NAME.getMessage());
}
}

public static class BlankPlayerNameException extends IllegalArgumentException {
public BlankPlayerNameException() {
super(ErrorMessage.NOT_ALLOW_BLANK.getMessage());
}
}

public static class NotAllowEmptyInputException extends IllegalArgumentException {
public NotAllowEmptyInputException() {
super(ErrorMessage.NOT_ALLOW_EMPTY.getMessage());
}
}

public static class InvalidPlayerNameLengthException extends IllegalArgumentException {
public InvalidPlayerNameLengthException() {
super(ErrorMessage.INVALID_NAME_LENGTH.getMessage());
}
}

public static class NotNumericException extends IllegalArgumentException {
public NotNumericException() {
super(ErrorMessage.NOT_NUMERIC.getMessage());
}
}

public static class NotNaturalNumberException extends IllegalArgumentException {
public NotNaturalNumberException() {
super(ErrorMessage.ONLY_NATURAL_NUMBER.getMessage());
}
}

public static class NotAllowZeroNumberException extends IllegalArgumentException {
public NotAllowZeroNumberException() {
super(ErrorMessage.NOT_ALLOW_ZERO.getMessage());
}
}

}
14 changes: 14 additions & 0 deletions src/main/java/model/Height.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package model;

public class Height {

private final String height;

public Height(final String height) {
this.height = height;
}

public String getHeight() {
return height;
}
}
24 changes: 24 additions & 0 deletions src/main/java/model/Ladder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package model;

import util.RandomFootholdGenerator;

public class Ladder {
private final StringBuilder lines;

Choose a reason for hiding this comment

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

StringBuilder는 싱글스레드에 적합할까요, 멀티스레드에 적합할까요?
그리고 StringBuilder에 final을 붙인 이유는 무엇인가요?!
위 질문에 대해 고민해보고, StringBuilder를 채택해야하는 이유를 생각해보세요!

private Line line;

public Ladder() {
this.lines = new StringBuilder();
this.line = new Line(new RandomFootholdGenerator());
}

public void makeLines(Height height, Players players) {
for (int i = 0; i < Integer.parseInt(height.getHeight()); i++) {
line.makeLine(players);
lines.append(line.getLine()).append("\n");
}
}

public StringBuilder getLines() {
return lines;
}
}
19 changes: 19 additions & 0 deletions src/main/java/model/LadderSymbol.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package model;


public enum LadderSymbol {

BLANK(" "),
FOOTHOLD("-----"),
BAR("|");

private final String symbol;

LadderSymbol(final String symbol) {
this.symbol = symbol;
}

public String getSymbol() {
return this.symbol;
}
}
73 changes: 73 additions & 0 deletions src/main/java/model/Line.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package model;

import util.FootholdGenerator;

public class Line {
private final StringBuilder line;
private static final int LAST_PLAYER_OFFSET = 1;
private static final String BLANK = LadderSymbol.BLANK.getSymbol();
private static final String BAR = LadderSymbol.BAR.getSymbol();
private static final String FOOTHOLD = LadderSymbol.FOOTHOLD.getSymbol();
private final FootholdGenerator footholdGenerator;

public Line(FootholdGenerator footholdGenerator) {
this.line = new StringBuilder();
this.footholdGenerator = footholdGenerator;
}

public void makeLine(Players players) {
initializeLine();

boolean previousWasFoothold = false;
int numOfPlayers = players.getPlayerCount();

for (int i = 0; i < numOfPlayers; i++) {
currentLadderLine(BAR);
previousWasFoothold = AppendFootholdIfNotLastPlayer(i, numOfPlayers, previousWasFoothold);
}
}

private boolean AppendFootholdIfNotLastPlayer(int currentPlayerIndex, int totalPlayers, boolean previousWasFoothold) {
if (isNotLastPlayer(currentPlayerIndex, totalPlayers)) {
return appendFoothold(previousWasFoothold);
}

return previousWasFoothold;
}

public StringBuilder getLine() {
return line;
}

private void initializeLine() {
line.delete(0, line.length());

currentLadderLine(BLANK);
}

private boolean isNotLastPlayer(int currentIndex, int totalPlayers) {
return currentIndex < totalPlayers - LAST_PLAYER_OFFSET;
}

private boolean appendFoothold(boolean hasPreviousFoothold) {
boolean isFootholdAppended = false;

if (canAppendFoothold(hasPreviousFoothold)) {
currentLadderLine(FOOTHOLD);
isFootholdAppended = true;
}

currentLadderLine(BLANK);

return isFootholdAppended;
}

private boolean canAppendFoothold(boolean hasPreviousFoothold) {
return !hasPreviousFoothold && footholdGenerator.generate();
}

private void currentLadderLine(String ladderSymbol) {
line.append(ladderSymbol);
}

}
15 changes: 15 additions & 0 deletions src/main/java/model/Player.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package model;

public class Player {

private final String playerName;

public Player(String playerName) {
this.playerName = playerName;
}

public String getName() {
return this.playerName;
}

}
26 changes: 26 additions & 0 deletions src/main/java/model/Players.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package model;

import java.util.List;

public class Players {

private final List<Player> players;

public Players(List<String> playerNames) {
this.players = makePlayers(playerNames);
}

public int getPlayerCount() {
return this.players.size();
}

public List<Player> getPlayers() {
return players;
}

private List<Player> makePlayers(final List<String> playerNames) {
return playerNames.stream()
.map(Player::new)
.toList();

Choose a reason for hiding this comment

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

16부터 toList() 가 생기면서 정말 편리해진 것 같아요!👍🏻

}
}
5 changes: 5 additions & 0 deletions src/main/java/util/FootholdGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package util;

public interface FootholdGenerator {
boolean generate();
}
13 changes: 13 additions & 0 deletions src/main/java/util/RandomFootholdGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package util;

import java.util.Random;

public class RandomFootholdGenerator implements FootholdGenerator{

private static Random random = new Random();

@Override
public boolean generate() {
return random.nextBoolean();
}
}
Loading