Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
680d774
[step3] refactor : (피드백 반영) @FunctionalInterface 추가
Jul 5, 2019
e361f56
[step3] refactor : (피드백 반영) BarGeneratorImpl 명칭 변경
Jul 5, 2019
daa0bf8
[step3] feat : (피드백 반영) List<Boolean>의 Boolean을 포장하기 위한 Bar 객체 생성
Jul 5, 2019
9a7128f
[step3] refactor : (피드백 반영) List<Boolean>의 Boolean을 Bar 객체로 포장해서 사용
Jul 5, 2019
af9ca67
[step3] refactor : (피드백 반영) OutputView 내부에 InputView에서 import받은 메서드를 …
Jul 5, 2019
2ef101a
[step3] refactor : (피드백 반영) String.format() 사용
Jul 5, 2019
6047e63
[step3] feat : 지네릭 인터페이스(NameFormatter)와 구현클래스(PlayerNameFormatter) 추…
Jul 5, 2019
f53137a
[step3] refactor : (피드백 반영) LineMaker 클래스의 generateBars() 메서드의 기능들을 명…
Jul 5, 2019
cc0def7
[step3] refactor : 상수 사용해서 의미 명시
Jul 5, 2019
d459ab6
[step3] refactor : (피드백 반영) generateBars() 메서드가 재사용될 수 있도록 수정
Jul 5, 2019
f4cec19
[step3] docs : 구현 기능 목록 업데이트
Jul 5, 2019
4b01e7c
[step3] feat : Prize(상품; 사다리의 결과) 객체 추가
Jul 5, 2019
3b0ebe1
[step3] feat : Prize(상품) 이름 예외처리 추가
Jul 5, 2019
2c6b1ac
[step3] feat : 상품들(Prizes) 객체 추가 및 예외처리
Jul 5, 2019
ae7057d
[step3] refactor : 예외처리 메서드 분리
Jul 5, 2019
8365d36
[step3] refactor : 생성시에 새로운 컬렉션에 담기 및 예외처리 메서드 분리
Jul 5, 2019
a0f0ec7
[step3] feat : 사다리 게임 실행 로직 작성중 (미완성)
Jul 5, 2019
a06e462
[step3] feat : 위치 데이터를 가지는 Position 객체 생성
Integerous Jul 5, 2019
2fece83
[step3] feat : 사다리 이동 로직에 Position 객체 사용 및 테스트 추가
Integerous Jul 5, 2019
2176f90
[step3] refactor : Position이 0보다 작거나 같은 경우 왼쪽으로 이동 못하는 예외 처리
Integerous Jul 5, 2019
e0e3115
[step3] feat : 실행 결과들(상품들)을 입력받아 Prizes 생성
Integerous Jul 6, 2019
cf6667f
[step3] feat : PrizeNameFormatter 생성해서 입력받은 상품들 출력
Integerous Jul 6, 2019
0cafc8f
[step3] refactor : formatter들을 패키지 생성해서 분리
Integerous Jul 6, 2019
3babbd1
[step3] feat : DataPrintFormatter 인터페이스와 구현체 생성하여 코드 중복 제거
Integerous Jul 6, 2019
86a3441
[step3] feat : 사다리 게임 실행 로직과 결과를 GameResult 객체로 분리
Integerous Jul 6, 2019
7586d14
[step3] feat : 개별 결과 입출력 및 개별 결과 찾는 로직 추가
Integerous Jul 6, 2019
f345856
[step3] feat : 플레이어 이름 비교 메서드 추가, 전체 결과 출력 로직 추가
Integerous Jul 6, 2019
828993f
[step3] docs : 구현 기능 목록 업데이트
Integerous Jul 6, 2019
a3ebeed
[step3] style : 코드 컨벤션 점검
Integerous Jul 6, 2019
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
19 changes: 14 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,34 @@

## 구현 기능 목록

- [x] 게임참가자(플레이어)의 이름을 입력받는다.
- [x] 1. 게임참가자(플레이어)의 이름을 입력받는다.
- [x] 입력된 이름들이 플레이어가 된다.
- [x] 플레이어의 이름은 (,)로 구분된다.
- [x] 예외 처리: (,) 사이의 공백 제거
- [x] 플레이어의 이름은 최대 5글자이다.
- [x] 예외 처리: 5글자가 넘거나 공백일 경우
- [x] 플레이어는 최소 2명 있어야 한다. (사다리의 다리는 최소 2개 이상이므로)
- [x] 예외 처리: 플레이어가 2명 미만일 경우
- [x] 사다리 높이를 입력받는다.
- [x] 2. 실행 결과(당첨 상품)를 입력받는다.
- [x] 예외 처리: 5글자가 넘거나 공백일 경우
- [x] 예외 처리: 플레이어의 수와 상품의 수가 같지 않을 경우
- [x] 3. 사다리 높이를 입력받는다.
- [x] 예외 처리: 사다리 높이가 1 미만일 경우
- [x] 플레이어들을 출력한다.
- [x] 4. 플레이어들을 출력한다.
- [x] 플레이어 이름
- [x] 플레이어 이름이 5글자 미만일 경우 부족한 만큼 왼쪽으로 공백을 채운다.
- [x] 사다리를 출력한다.
- [x] 5. 사다리를 출력한다.
- [x] 사다리의 열을 생성한다.
- [x] 각 열은 기둥과 바(bar)로 이루어져있다.
- [x] 기둥 사이를 잇는 바를 랜덤으로 생성한다.
- [x] 바는 연속될 수 없다.
- [x] 바는 '-' 5개로 이루어져있다.
- [x] 입력받은 높이만큼 열을 추가한다.

- [x] 6. 당첨 상품 들을 출력한다.
- [x] 7. 사다리 게임을 실행한다.
- [x] 사용자의 현재 위치를 기준으로 사다리를 타고, 최종 위치를 계산한다.
- [x] 사용자의 현재 위치와 bar의 위치가 1만큼 작으면 사용자의 현재위치는 1만큼 감소
- [x] 사용자의 현재 위치보다 bar의 위치가 같으면 사용자의 현재위치가 1만큼 증가
- [x] 8. 결과를 보고싶은 플레이어(혹은 전체)를 묻고 입력받는다.
- [x] 9. 해당 플레이어(혹은 전체)의 실행 결과를 반환한다.

18 changes: 14 additions & 4 deletions src/main/java/ladder/Application.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
package ladder;

import ladder.domain.Height;
import ladder.domain.Ladder;
import ladder.domain.Players;
import ladder.domain.*;
import ladder.view.InputView;
import ladder.view.OutputView;

import java.util.HashMap;
import java.util.Map;

public class Application {

public static void main(String[] args) {
Players players = Players.of(InputView.askPlayers());
Prizes prizes = Prizes.from(InputView.askPrizes(), players.numberOfPlayers());
Copy link

Choose a reason for hiding this comment

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

PlayersPrizes를 가진 GameInfo 클래스를 만들어 보면 어떨까요?

Height height = Height.from(InputView.askHeight());

Ladder ladder = Ladder.from(players, height);
OutputView.drawLadder(players, ladder, prizes);

GameResult gameResult = GameResult.of(players, ladder, prizes);
String wantedPlayer = InputView.askWhichResultWant();

OutputView.printResult(players, ladder);
if ("all".equals(wantedPlayer)) {
Copy link

Choose a reason for hiding this comment

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

매직 넘버란 코드 안에 작성된 구체적인 숫자 혹은 문자열을 말합니다. 그리고 매직 넘버의 단점은 아래와 같습니다.

  • 의미를 이해하기 어렵다.
  • 복수의 장소에서 사용된다면 변경하기 힘들다.

그런 매직 넘버는 의미를 나타낼 수 있는 상수로 치환하여 코드의 가독성을 높여 보는 것은 어떨까요?

OutputView.printAllResult(gameResult);
return;
}
OutputView.printSingleResult(gameResult.findResult(wantedPlayer));
}
}
18 changes: 18 additions & 0 deletions src/main/java/ladder/domain/Bar.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ladder.domain;

public class Bar {
Copy link

Choose a reason for hiding this comment

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

"Bar는 방향을 표현하는 Direction을 가진다."라는 설계는 어떨까요?

Copy link
Author

@Integerous Integerous Jul 8, 2019

Choose a reason for hiding this comment

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

질문이 있습니다..!!

이 부분은 아직 제대로 이해가 안되서 바로 Bar 객체를 뜯어고치기보다
먼저 구현한 방법이 어떤 문제를 가지고 있는지, 개선이 필요한 이유를 여쭙고 싶은데요..!

저의 얕은 시야로는 Bar에 방향을 표현하는 Direction을 가지게 하는 것이
Bar 객체의 성질(?)과는 어울리지 않게 팔 다리를 하나씩 더 붙이는 느낌이 들었습니다..

Bar는 Point 2개를 사용하는 방법과는 조금 다르게 Bar들을 배열로 생각하고
Bar의 index를 플레이어의 index와 비교해서 (플레이어의 위치도 고정된 배열이므로)
같으면 플레이어의 위치+1
Bar의 index가 플레이어의 index보다 1만큼 왼쪽에 있으면 플레이어의 위치-1
이 방법인데요..

Point 2개를 사용하면 사다리를 타서 플레이어의 위치를 변화시킨다는 핵심 로직이
true와 false로 표현되기 때문에 더 복잡하고 명시적이지 않은 것 같아서 Bar를 사용했고,
아래와 같이 구현했었습니다.

Position travel(Position position) {
    if (bars.get(position.getPosition()).isExist()) {
        return position.moveToRight(); // 사용자와 같은 위치에 bar가 있으면 사용자 위치 1 증가
    }
    if (position.isMovableToLeft()
            && bars.get(position.getLeftPosition()).isExist()) {
        return position.moveToLeft(); // 사용자의 왼쪽에 bar가 있으면 사용자 위치 1 감소
    }
    return position; // 위의 경우가 아니면 위치 유지(패스)
}

당연히 문제가 있기 때문에 Bar 객체가 방향을 가지도록 조언을 주셨을 것 같은데요!
위와 같이 구현할 경우에 어떤 문제가 있을지 궁금합니다..!

(수업시간에 강사님께서 Point 2개로 구현하시는 것을 배우긴했지만..
나중에 연습 삼아 다시 구현해도 Point 2개보다는 Bar 1개로 구현하고 싶을 것 같아서
문제점을 알고 제 생각을 바로 잡고 싶습니다..)

Copy link

Choose a reason for hiding this comment

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

질문에 대한 답변은 Slack을 통해 남겼어요. 👍


private final boolean bar;

private Bar(boolean bar) {
this.bar = bar;
}

public static Bar from(boolean doesBarExist) {
return new Bar(doesBarExist);
}

public boolean isExist() {
return bar;
}
}
1 change: 1 addition & 0 deletions src/main/java/ladder/domain/BarGenerator.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ladder.domain;

@FunctionalInterface
public interface BarGenerator {

boolean generateBar();
Expand Down
46 changes: 46 additions & 0 deletions src/main/java/ladder/domain/GameResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package ladder.domain;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class GameResult {
private static final String ALERT_FOR_INVALID_PLAYER_NAME = "입력하신 플레이어는 존재하지 않습니다.";

private final Map<Player, Prize> resultMap;

private GameResult(Map<Player, Prize> resultMap) {
this.resultMap = new HashMap<>(resultMap);
}

public static GameResult of(Players players, Ladder ladder, Prizes prizes) {
return new GameResult(playGame(players, ladder, prizes));
}

private static Map<Player, Prize> playGame(Players players, Ladder ladder, Prizes prizes) {
Map<Player, Prize> resultMap = new HashMap<>();
int numberOfPlayers = players.numberOfPlayers();

for (int i = 0; i < numberOfPlayers; i++) {
Player player = players.getPlayers().get(i);
Copy link

Choose a reason for hiding this comment

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

Players에 메시지를 던지도록 구조를 바꿔 보면 어떨까요? 객체에 메시지를 보내 데이터를 가지는 객체가 일하도록 해보세요.

Position finalPosition = ladder.goThroughLinesFrom(Position.from(i));
Prize prize = prizes.getPrizes().get(finalPosition.getPosition());

resultMap.put(player, prize);
}
return resultMap;
}

public String findResult(String wantedPlayer) {
Player playerWanted = resultMap.keySet().stream()
.filter(player -> player.isSame(wantedPlayer))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException(ALERT_FOR_INVALID_PLAYER_NAME));

return resultMap.get(playerWanted).getPrize();
}

public Map<Player, Prize> getResultMap() {
return Collections.unmodifiableMap(resultMap);
}
}
12 changes: 8 additions & 4 deletions src/main/java/ladder/domain/Height.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ public class Height {
private final int height;

private Height(int inputHeight) {
if (inputHeight < MINIMUM_LADDER_HEIGHT) {
throw new IllegalArgumentException(ALERT_MINIMUM_LADDER_HEIGHT);
}
validationHeight(inputHeight);
this.height = inputHeight;
}

public static Height from(int inputHeight) {
return new Height(inputHeight);
}

public int getHeight() {
private void validationHeight(int inputHeight) {
if (inputHeight < MINIMUM_LADDER_HEIGHT) {
throw new IllegalArgumentException(ALERT_MINIMUM_LADDER_HEIGHT);
}
}

int getHeight() {
return height;
}
}
10 changes: 9 additions & 1 deletion src/main/java/ladder/domain/Ladder.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ladder.domain;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -10,7 +11,7 @@ public class Ladder {
private List<Line> lines;

private Ladder(List<Line> lines) {
this.lines = lines;
this.lines = new ArrayList<>(lines);
}

public static Ladder from(Players players, Height height) {
Expand All @@ -20,6 +21,13 @@ public static Ladder from(Players players, Height height) {
.collect(Collectors.toList()));
}

Position goThroughLinesFrom(Position position) {
for (Line line : lines) {
position = line.travel(position);
}
return position;
}

public List<Line> getLines() {
return Collections.unmodifiableList(lines);
}
Expand Down
17 changes: 14 additions & 3 deletions src/main/java/ladder/domain/Line.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@

public class Line {

private List<Boolean> bars;
private List<Bar> bars;

private Line(List<Boolean> bars) {
Line(List<Bar> bars) {
this.bars = new ArrayList<>(bars);
}

Expand All @@ -17,7 +17,18 @@ public static Line from(int numberOfPlayers) {
return new Line(lineMaker.generateBars(numberOfPlayers));
}

public List<Boolean> getBars() {
public List<Bar> getBars() {
return Collections.unmodifiableList(bars);
}

Position travel(Position position) {
if (bars.get(position.getPosition()).isExist()) { //사용자의 현재 위치와 bar의 위치가 같으면 사용자의 현재위치가 1만큼 증가
return position.moveToRight();
}
if (position.getPosition() > 0 && bars.get(position.getLeftPosition()).isExist()) { //사용자의 현재 위치보다 bar의 위치가 1만큼 작으면 사용자의 현재위치는 1만큼 감소
return position.moveToLeft();
}
return position;
} //TODO: 이 로직을 Bar 객체로 위임하고 싶은데, Bar 2개를 사용해서 위치를 갱신하는 방식이라 이 곳에서 처리하는 상태

}
41 changes: 26 additions & 15 deletions src/main/java/ladder/domain/LineMaker.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,40 @@
import java.util.List;

class LineMaker {
private static final int NUMBER_OF_FIRST_AND_LAST_BAR = 2;

private List<Boolean> randomBars = new ArrayList<>();
private BarGeneratorImpl barGenerator = new BarGeneratorImpl();
private List<Bar> randomBars;
private RandomBarGenerator barGenerator = new RandomBarGenerator();

List<Boolean> generateBars(int numberOfPlayers) {
randomBars.add(barGenerator.generateBar());
for (int i = 1; i < numberOfPlayers - 1; i++) {
boolean previousBarExist = randomBars.get(i - 1);
addNextBar(previousBarExist);
}
addBlankBar();
List<Bar> generateBars(int numberOfPlayers) {
this.randomBars = new ArrayList<>();
generateFirstBar();
generateMiddleBars(numberOfPlayers);
generateLastBar();
return randomBars;
}

private void addNextBar(boolean previousBarExist) {
if (previousBarExist) {
addBlankBar();
private void generateFirstBar() {
randomBars.add(Bar.from(barGenerator.generateBar()));
}

private void generateMiddleBars(int numberOfPlayers) {
int spaceForMiddleBars = numberOfPlayers - NUMBER_OF_FIRST_AND_LAST_BAR;
for (int i = 0; i < spaceForMiddleBars; i++) {
Bar previousBar = randomBars.get(i);
addNextBar(previousBar);
}
}

private void addNextBar(Bar previousBar) {
if (previousBar.isExist()) {
generateLastBar();
return;
}
randomBars.add(barGenerator.generateBar());
randomBars.add(Bar.from(barGenerator.generateBar()));
}

private void addBlankBar() {
randomBars.add(false);
private void generateLastBar() {
Copy link

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.

마지막 Bar는 모두 비어있는 Bar라서 generateLastBar()로 이름지었었는데요,
비어있는 바를 생성하는 메서드니까 generateEmptyBar()로 고치면 조언주신 의도에 부합할까요..?

Copy link

Choose a reason for hiding this comment

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

네, 맞습니다. 👍

randomBars.add(Bar.from(Boolean.FALSE));
}
}
4 changes: 4 additions & 0 deletions src/main/java/ladder/domain/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ private void validationPlayerName(String name) {
}
}

boolean isSame(String wantedPlayer) {
return wantedPlayer.equals(name);
}

public String getName() {
return name;
}
Expand Down
13 changes: 9 additions & 4 deletions src/main/java/ladder/domain/Players.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ladder.domain;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
Expand All @@ -13,10 +14,8 @@ public class Players {
private final List<Player> players;

private Players(List<Player> players) {
if (players.size() < MINIMUM_NUMBER_OF_PLAYERS) {
throw new IllegalArgumentException(ALERT_SHORTAGE_OF_NUMBER_OF_PLAYERS);
}
this.players = players;
validationPlayers(players);
this.players = new ArrayList<>(players);
}

public static Players of(String inputNames) {
Expand All @@ -26,6 +25,12 @@ public static Players of(String inputNames) {
.collect(Collectors.toList()));
}

private void validationPlayers(List<Player> players) {
if (players.size() < MINIMUM_NUMBER_OF_PLAYERS) {
throw new IllegalArgumentException(ALERT_SHORTAGE_OF_NUMBER_OF_PLAYERS);
}
}

public int numberOfPlayers() {
return players.size();
}
Expand Down
49 changes: 49 additions & 0 deletions src/main/java/ladder/domain/Position.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package ladder.domain;

public class Position {

private static final int MOVE_ONE_STEP_TO_RIGHT = 1;
private static final int MOVE_ONE_STEP_TO_LEFT = -1;
static final String ALERT_UNMOVABLE_TO_LEFT = "더 이상 왼쪽으로 이동할 수 없습니다.";
private int position;

private Position(int currentPosition) {
this.position = currentPosition;
}

public static Position from(int currentPosition) {
return new Position(currentPosition);
}

public boolean isSameWith(int targetPosition) {
return position == targetPosition;
}

Position moveToRight() {
return from(this.position + MOVE_ONE_STEP_TO_RIGHT);
}

Position moveToLeft() {
validationMovableToLeft();
return from(this.position + MOVE_ONE_STEP_TO_LEFT);
}

int getPosition() {
return position;
}

int getLeftPosition() {
validationMovableToLeft();
return this.position + MOVE_ONE_STEP_TO_LEFT;
}

private void validationMovableToLeft() {
if (unmovableToLeft()) {
throw new IllegalArgumentException(ALERT_UNMOVABLE_TO_LEFT);
}
}

private boolean unmovableToLeft() {
return position <= 0;
}
}
Loading