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단계 - 사다리(생성) #1562

Open
wants to merge 10 commits into
base: yerin-158
Choose a base branch
from
Open
14 changes: 13 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,17 @@
* 코드 리뷰 피드백에 대한 개선 작업을 하고 다시 PUSH한다.
* 모든 피드백을 완료하면 다음 단계를 도전하고 앞의 과정을 반복한다.

## 기능 목록
- [x] 쉼표로 구분된 참여할 사람을 입력 받는다. (단, 이름은 최대 5자까지 부여)
- [x] 최대 사다리 높이 개수를 입력 받는다.
- [x] 입력 받은 사람과 사다리 높이를 이용해 사다리를 출력한다.

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

## 온라인 코드 리뷰 과정
* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/nextstep-step/nextstep-docs/tree/master/codereview)
* [텍스트와 이미지로 살펴보는 온라인 코드 리뷰 과정](https://github.com/nextstep-step/nextstep-docs/tree/master/codereview)
16 changes: 16 additions & 0 deletions src/main/java/ladderapplication/LadderApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ladderapplication;

import ladderapplication.models.Ladder;
import ladderapplication.models.requests.GameSettingRequest;
import ladderapplication.ui.Printer;

public class LadderApplication {

public static void main(String[] args) {
GameSettingRequest gameSettingRequest = Printer.requestGameSetting();

Ladder ladder = Ladder.from(gameSettingRequest);
ladder.print();
}

}
57 changes: 57 additions & 0 deletions src/main/java/ladderapplication/models/Ladder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package ladderapplication.models;

import ladderapplication.models.requests.GameSettingRequest;
import ladderapplication.utils.DecoratingUtils;

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

public class Ladder {

private final List<Player> players;
private final List<Line> lines;
private final String drawing;
Comment on lines +10 to +14

Choose a reason for hiding this comment

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

사다리 객체에 필요한 정보가 어떤것인지 정리가 필요할거같아요~
필요한 최소의 정보만을 유지하는게 어떨까요? 불필요한 필드를 가짐으로써 응집력이 낮아져 유지보수를 힘들게 할 수 있습니다.


private Ladder(List<Player> players, List<Line> lines, String drawing) {
this.players = players;
this.lines = lines;
this.drawing = drawing;
}

public static Ladder from(GameSettingRequest gameSettingRequest) {
List<Player> newPlayers = gameSettingRequest.getPlayerRequests()
.stream()
.map(Player::from)
.collect(Collectors.toList());

int playerCount = gameSettingRequest.getPlayerRequests().size();
int ladderHeight = gameSettingRequest.getLadderRequest().getHeight();
List<Line> newLines = Stream.generate(() -> Line.of(playerCount))
.limit(ladderHeight)
.collect(Collectors.toList());
Comment on lines +22 to +32

Choose a reason for hiding this comment

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

위 내용을 개선하면 사다리 생성에 있어서 불필요한 로직들도 분리될거에요!

Choose a reason for hiding this comment

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

GameSettingRequest는 client의 입력을 가지는 객체인데요, 필요한 정보만을 넘기는것은 어떨까요?
외부 변화에 취약한 단점이 있어보여요.

(추가로 테스트 코드 작성하실때도 불편하시지 않았나요?)

Choose a reason for hiding this comment

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

테스트코드가 없군요!
도메인에 대한 테스트도 꼭 작성해주셔요 😊


String drawing = createDrawing(newPlayers, newLines);
return new Ladder(newPlayers, newLines, drawing);
}

private static String createDrawing(List<Player> players, List<Line> lines) {
StringBuilder sb = new StringBuilder();
addPlayerNames(sb, players);
addLines(sb, lines);
return sb.toString();
}
Comment on lines +38 to +43

Choose a reason for hiding this comment

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

도메인이 뷰에 대한 책임까지 가지고있는 구조가 맞을까요?


private static void addPlayerNames(StringBuilder sb, List<Player> players) {
players.forEach(player -> sb.append(DecoratingUtils.getDecoratedName(player.getName())));
sb.append("\n");
}

private static void addLines(StringBuilder sb, List<Line> lines) {
lines.forEach(line -> sb.append(line.print()).append("\n"));
}

public void print() {
System.out.println(this.drawing);
}
Comment on lines +54 to +56

Choose a reason for hiding this comment

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

이런부분들도 view쪽에서 담당하게 해보면 어떨까요?

}
57 changes: 57 additions & 0 deletions src/main/java/ladderapplication/models/Line.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package ladderapplication.models;

import ladderapplication.utils.DecoratingUtils;

import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class Line {

Choose a reason for hiding this comment

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

각 도메인에 대한 테스트도 꼭 작성해주셔요!

private static final Random RANDOM_GENERATOR = new Random();
private static final int HAS_STEP_BOUND = 4;
private static final int RANDOM_NUMBER_BOUND = 10;
private final List<Boolean> hasSteps;

private Line(List<Boolean> hasSteps) {
this.hasSteps = hasSteps;
}

public static Line of(int playerCount) {
List<Boolean> hasSteps = Stream.generate(() -> Boolean.FALSE)
.limit(playerCount - 1)
.collect(Collectors.toList());

IntStream.range(0, hasSteps.size())
.forEach(index -> setHasStep(hasSteps, index));

return new Line(hasSteps);
}

private static void setHasStep(List<Boolean> hasSteps, int index) {
if (prevLadderHasNotStep(hasSteps, index) && hasStep()) {
hasSteps.set(index, Boolean.TRUE);
}
}

private static boolean prevLadderHasNotStep(List<Boolean> hasSteps, int index) {
return index == 0 || !hasSteps.get(index - 1);
}

private static boolean hasStep() {
return RANDOM_GENERATOR.nextInt(RANDOM_NUMBER_BOUND) >= HAS_STEP_BOUND;
}

public String print() {
StringBuilder sb = new StringBuilder();
sb.append(" ");
hasSteps.forEach(hasStep -> {
Comment on lines +46 to +49

Choose a reason for hiding this comment

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

Ladder쪽과 같은 내용이에요~ 책임분리를 해보아요!

sb.append("|")
.append(DecoratingUtils.getStep(hasStep));
});

sb.append("|");
return sb.toString();
}
}
20 changes: 20 additions & 0 deletions src/main/java/ladderapplication/models/Player.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ladderapplication.models;

import ladderapplication.models.requests.PlayerRequest;

public class Player {

private final String name;

private Player(String name) {
this.name = name;
}

public String getName() {
return name;
}

public static Player from(PlayerRequest playerRequest) {
return new Player(playerRequest.getName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ladderapplication.models.requests;

Choose a reason for hiding this comment

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

requests 패키지 위치도 도메인 하위가 아닌 최소한 같은 레벨로 올려야하지 않을까요?


import java.util.List;

public class GameSettingRequest {

private final LadderRequest ladderRequest;
private final List<PlayerRequest> playerRequests;

private GameSettingRequest(LadderRequest ladderRequest, List<PlayerRequest> playerRequests) {
this.ladderRequest = ladderRequest;
this.playerRequests = playerRequests;
}

public LadderRequest getLadderRequest() {
return ladderRequest;
}

public List<PlayerRequest> getPlayerRequests() {
return playerRequests;
}

public static GameSettingRequest of(LadderRequest ladderRequest, List<PlayerRequest> playerRequests) {
return new GameSettingRequest(ladderRequest, playerRequests);
}
}
19 changes: 19 additions & 0 deletions src/main/java/ladderapplication/models/requests/LadderRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ladderapplication.models.requests;

public class LadderRequest {

private final int height;

private LadderRequest(int height) {
this.height = height;
}

public int getHeight() {
return height;
}

public static LadderRequest of(int height) {
return new LadderRequest(height);
}

}
18 changes: 18 additions & 0 deletions src/main/java/ladderapplication/models/requests/PlayerRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ladderapplication.models.requests;

public class PlayerRequest {

private final String name;

private PlayerRequest(String name) {
this.name = name;
}

public String getName() {
return name;
}

public static PlayerRequest of(String name) {
return new PlayerRequest(name);
}
}
17 changes: 17 additions & 0 deletions src/main/java/ladderapplication/ui/InputScanner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ladderapplication.ui;

import java.util.Scanner;

public class InputScanner {

private static final Scanner SCANNER = new Scanner(System.in);

public static String stringScan() {
return SCANNER.nextLine();
}

public static int intScan() {
return SCANNER.nextInt();
}

}
25 changes: 25 additions & 0 deletions src/main/java/ladderapplication/ui/Printer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package ladderapplication.ui;

import ladderapplication.models.requests.GameSettingRequest;
import ladderapplication.models.requests.LadderRequest;
import ladderapplication.models.requests.PlayerRequest;

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

public class Printer {

public static GameSettingRequest requestGameSetting() {
System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)");
String players = InputScanner.stringScan();
String[] playerArr = players.split(",");
List<PlayerRequest> playerRequests = Stream.of(playerArr).map(PlayerRequest::of).collect(Collectors.toList());

System.out.println("최대 사다리 높이는 몇 개인가요?");
int height = InputScanner.intScan();
LadderRequest ladderRequest = LadderRequest.of(height);

return GameSettingRequest.of(ladderRequest, playerRequests);
}
}
32 changes: 32 additions & 0 deletions src/main/java/ladderapplication/utils/DecoratingUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ladderapplication.utils;

public class DecoratingUtils {

private static final String EMPTY = " ";
private static final String STEP_PIECE = "-";
private static final int NAME_SPACE = 6;
private static final int STANDARD_SIZE = 4;
private static final int STEP_LENGTH = 5;

public static String getDecoratedName(String name) {
StringBuilder sb = new StringBuilder();
if (name.length() < STANDARD_SIZE) {
sb.append(getRepeatCharacter(EMPTY, STANDARD_SIZE - name.length()));
}
sb.append(name)
.append(getRepeatCharacter(EMPTY, NAME_SPACE - sb.length()));
return sb.toString();
}

private static String getRepeatCharacter(String character, int repeatCount) {
return character.repeat(repeatCount);
}

public static String getStep(boolean hasStep) {
if (hasStep) {
return getRepeatCharacter(STEP_PIECE, STEP_LENGTH);
}

return getRepeatCharacter(EMPTY, STEP_LENGTH);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ladderapplication.validator;

import ladderapplication.models.requests.PlayerRequest;

public class PlayerRequestValidator {

private static final String WRONG_NAME = "플레이어 이름음 5글자 이하 여야 합니다.";

public static void validation(PlayerRequest playerRequest) {
if (playerRequest.getName().length() > 5) {
throw new IllegalArgumentException(WRONG_NAME);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ladderapplication.validator;

import ladderapplication.models.requests.PlayerRequest;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

class PlayerRequestValidatorTest {

private static final String WRONG_NAME = "플레이어 이름음 5글자 이하 여야 합니다.";
private static final List<PlayerRequest> playerRequests = new ArrayList<>();

@BeforeAll
public static void setPlayerRequest() {
String players = "pobi,honux,crong,jk";
String[] playerArr = players.split(",");
Stream.of(playerArr).forEach(name -> playerRequests.add(PlayerRequest.of(name)));
}

@Test
@DisplayName("입력받은 사용자 이름이 5글자가 넘으면 Exception이 발생한다.")
void test1() {
PlayerRequest playerRequest = PlayerRequest.of("robinson");
Exception exception = assertThrows(IllegalArgumentException.class, () -> PlayerRequestValidator.validation(playerRequest));
assertThat(exception.getMessage()).isEqualTo(WRONG_NAME);

}
}