Skip to content

사다리타기: 2단계 - 사다리(생성) #2357

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

Open
wants to merge 7 commits into
base: hongyeseul
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
33 changes: 33 additions & 0 deletions src/main/java/ladder/LadderGameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package ladder;

import ladder.domain.ladder.Ladder;
import ladder.domain.player.Players;
import ladder.view.input.ConsoleInputView;
import ladder.view.input.InputView;
import ladder.view.output.OutputView;

public class LadderGameController {

private final InputView inputView;
private final OutputView outputView;

public LadderGameController(InputView inputView, OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}

public static void main(String[] args) {
LadderGameController game = new LadderGameController(new ConsoleInputView(), new OutputView());
game.run();
}

private void run() {
String inputNames = inputView.inputPlayerNames();
Players players = new Players(inputNames);

int height = inputView.inputLadderHeight();
Ladder ladder = new Ladder(height, players.count());

outputView.printResult(players, ladder);
}
}
34 changes: 34 additions & 0 deletions src/main/java/ladder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## 2단계 - 사다리(생성)

### 기능 요구사항
- 사다리 게임에 참여하는 사람에 이름을 최대5글자까지 부여할 수 있다. 사다리를 출력할 때 사람 이름도 같이 출력한다.
- 사람 이름은 쉼표(,)를 기준으로 구분한다.
- 사람 이름을 5자 기준으로 출력하기 때문에 사다리 폭도 넓어져야 한다.
- 사다리 타기가 정상적으로 동작하려면 라인이 겹치지 않도록 해야 한다.
- `|-----|-----|` 모양과 같이 가로 라인이 겹치는 경우 어느 방향으로 이동할지 결정할 수 없다.


### 프로그래밍 요구사항

- 자바 8의 스트림과 람다를 적용해 프로그래밍한다.
- 규칙 6: 모든 엔티티를 작게 유지한다.

### 실행 결과
위 요구사항에 따라 4명의 사람을 위한 5개 높이 사다리를 만들 경우, 프로그램을 실행한 결과는 다음과 같다.

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

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

실행결과

pobi honux crong jk
|-----| |-----|
| |-----| |
|-----| | |
| |-----| |
|-----| |-----|
```
19 changes: 19 additions & 0 deletions src/main/java/ladder/domain/ladder/Ladder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ladder.domain.ladder;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

public class Ladder {
private final List<Line> lines;

public Ladder(int height, int countOfPlayers) {
lines = new ArrayList<>();
IntStream.range(0, height)

Choose a reason for hiding this comment

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

stream의 종단 연산은 안티패턴입니다 😄

lines = new ArrayList<>()로 별도 선언하고 추가하기보다는 map으로 바로 만들어 할당해볼 수 있을 것 같아요 :)

.forEach(i -> lines.add(new Line(countOfPlayers)));
}

public List<Line> getLines() {
return lines;
}
}
29 changes: 29 additions & 0 deletions src/main/java/ladder/domain/ladder/Line.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ladder.domain.ladder;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Line {
private final List<Point> points;

public Line(int countOfPlayers) {
int numberOfPoints = countOfPlayers - 1;
this.points = initializePoints(numberOfPoints);
}

private List<Point> initializePoints(int numberOfPoints) {
boolean previousHasLine = false;
List<Point> points = new ArrayList<>();
for (int i = 0; i < numberOfPoints; i++) {
Point point = Point.create(previousHasLine);
points.add(point);
previousHasLine = point.hasLine();
}
return Collections.unmodifiableList(points);
}

public List<Point> getPoints() {
return points;
}
}
18 changes: 18 additions & 0 deletions src/main/java/ladder/domain/ladder/Point.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ladder.domain.ladder;

public class Point {
private final boolean hasLine;

public Point(boolean hasLine) {
this.hasLine = hasLine;
}

public boolean hasLine() {
return hasLine;
}

public static Point create(boolean previousHasLine) {
if (previousHasLine) return new Point(false);
return new Point(Math.random() > 0.5);

Choose a reason for hiding this comment

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

Math.random()으로 인해서 테스트하기 힘든 구조가 될 것 같아요! 자동차때처럼 테스트가능하도록 해보면 어떨까요?

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

public class Player {
private static final int MAX_NAME_LENGTH = 5;
private final String name;

public Player(String name) {
if (name.length() > MAX_NAME_LENGTH) {
throw new IllegalArgumentException("이름은 최대 5자까지 가능합니다.");
}
this.name = name;
}

public String getName() {
return name;
}
}
26 changes: 26 additions & 0 deletions src/main/java/ladder/domain/player/Players.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ladder.domain.player;

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

public class Players {

Choose a reason for hiding this comment

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

Players도 테스트되면 좋겠네요!

private static final String DELIMITER_COMMA = ",";

private final List<Player> players;

public Players(String input) {
this.players = Arrays.stream(input.split(DELIMITER_COMMA))
.map(String::trim)
.map(Player::new)
.collect(Collectors.toList());
}

public int count() {
return players.size();
}

public List<Player> getPlayers() {
return players;
}
}
4 changes: 4 additions & 0 deletions src/main/java/ladder/view/input/BaseInputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package ladder.view.input;

public abstract class BaseInputView implements InputView {

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.

후에 리팩토링을 위해 만들어 둔 것입니다.
동일한 로직이 있을 경우 이곳에 추가하기 위해 만들어 두었습니다.

}
24 changes: 24 additions & 0 deletions src/main/java/ladder/view/input/ConsoleInputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ladder.view.input;

import java.util.Scanner;

public class ConsoleInputView extends BaseInputView {
private static final Scanner scanner = new Scanner(System.in);

@Override
public String read() {
return scanner.nextLine();
}

@Override
public String inputPlayerNames() {
System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)");
return read();
}

@Override
public int inputLadderHeight() {
System.out.println("최대 사다리 높이는 몇 개인가요?");
return Integer.parseInt(read());
}
}
7 changes: 7 additions & 0 deletions src/main/java/ladder/view/input/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ladder.view.input;

public interface InputView {
String read();
String inputPlayerNames();
int inputLadderHeight();
}
36 changes: 36 additions & 0 deletions src/main/java/ladder/view/output/OutputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ladder.view.output;

import ladder.domain.ladder.Ladder;
import ladder.domain.ladder.Line;
import ladder.domain.ladder.Point;
import ladder.domain.player.Players;

public class OutputView {

public static void printResult(Players players, Ladder ladder) {
System.out.println("\n실행결과\n");

printPlayerNames(players);
printLadder(ladder);
}

private static void printPlayerNames(Players players) {
players.getPlayers()
.forEach(p -> System.out.printf("%-6s", p.getName()));
System.out.println();
}

private static void printLadder(Ladder ladder) {
ladder.getLines().forEach(OutputView::printLine);
}

private static void printLine(Line line) {
StringBuilder sb = new StringBuilder();
for (Point point : line.getPoints()) {
sb.append("|").append(point.hasLine() ? "-----" : " ");
}
sb.append("|");
System.out.println(sb);
}

}
26 changes: 26 additions & 0 deletions src/test/java/ladder/domain/player/PlayerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ladder.domain.player;

import ladder.view.fake.FakeInputView;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy;

class PlayerTest {

@Test
@DisplayName("참여자의 이름이 5글자가 넘으면 예외가 발생한다.")
void getPlayerName() {
FakeInputView inputView = new FakeInputView("yeseul");
assertThatThrownBy(() -> new Player(inputView.inputPlayerNames()))
.isInstanceOf(IllegalArgumentException.class);
}

@Test
@DisplayName("참여자의 이름은 쉼표가 아닌 다른 구분자가 사용되면 예외가 발생한다.")

Choose a reason for hiding this comment

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

Players테스트가 여기있었네요. 테스트의 경우에는 클래스별로 만들어주셔야 어떤 테스트를 하려고하는지가 조금 더 명시적일 것 같아요. 클래스마다 테스트를 분리해보면 좋겠네요 😄

void getSplitter() {
FakeInputView inputView = new FakeInputView("yeseul,pobi|crong");

Choose a reason for hiding this comment

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

new Players("yeseul,pobi|crong")로도 충분히 테스트가 되지 않을까요? 단위테스트는 최대한 불필요한 다른 의존성은 지우고 테스트하고자하는 대상에 집중해보면 좋을 것 같아요!

assertThatThrownBy(() -> new Players(inputView.inputPlayerNames()))
.isInstanceOf(IllegalArgumentException.class);
}
}
27 changes: 27 additions & 0 deletions src/test/java/ladder/view/fake/FakeInputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package ladder.view.fake;

import ladder.view.input.BaseInputView;

public class FakeInputView extends BaseInputView {

private final String input;

public FakeInputView(String input) {
this.input = input;
}

@Override
public String read() {
return input.trim();
}

@Override
public String inputPlayerNames() {
return this.input;
}

@Override
public int inputLadderHeight() {
return Integer.parseInt(this.input);
}
}