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

[1단계 - 사다리 생성] 제이(이재윤) 미션 제출합니다. #112

Merged
merged 32 commits into from
Feb 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7d4b52f
docs: 기능구현목록 초안 작성
sosow0212 Feb 14, 2023
776c4c6
feat: Name에 대한 예외 로직 작성
sosow0212 Feb 14, 2023
da9d985
feat: Player 생성 로직 추가
sosow0212 Feb 14, 2023
3623a53
feat: Players 예외처리 및 가장 긴 이름 반환하는 로직 구현
sosow0212 Feb 14, 2023
fa48dea
feat: 사다리 높이에 대한 예외 로직 추가
sosow0212 Feb 14, 2023
525e8a3
feat: 발판 생성 기능 구현
sosow0212 Feb 15, 2023
b19130b
feat: 사다리의 각 Line을 생성 기능 구현
sosow0212 Feb 15, 2023
4a22c99
feat: 사다리 생성 기능 구현
sosow0212 Feb 15, 2023
2c159d1
feat: 높이와 너비를 가지는 Ladder도메인 생성
sosow0212 Feb 15, 2023
5e37d3b
feat: 입력을 받기 위한 기능 구현
sosow0212 Feb 15, 2023
be28c1b
refactor: players의 생성자 매개변수 타입 변경
sosow0212 Feb 15, 2023
f668103
feat: 컨트롤러에서 도메인 초기화 기능 구현
sosow0212 Feb 15, 2023
3338518
feat: 사다리타기 결과 출력 구현 (리팩토링 전)
sosow0212 Feb 15, 2023
09a7aac
refactor: OutputView 메서드 분리
sosow0212 Feb 15, 2023
38060ee
refactor: PlayerName에 대한 메서드 분리, 리네이밍, 예외메시지 추가
sosow0212 Feb 16, 2023
8ece83b
refactor: Players에 대한 메서드 분리, 예외메시지 추가
sosow0212 Feb 16, 2023
fd87cf9
refactor: 사다리 높이 메서드 분리 및 예외 메시지 추가
sosow0212 Feb 16, 2023
fb926f8
refactor: Lines, Line 리네이밍 작업
sosow0212 Feb 16, 2023
6a9e343
refactor: 테스트를 위한 Lines 생성자 추가 및 메서드 분리, 테스트 리팩토링 작업
sosow0212 Feb 16, 2023
d3762e8
refactor: Line 메서드 분리 및 리네이밍 진행
sosow0212 Feb 16, 2023
08f3d76
feat: 최대 사다리 높이에 대한 예외처리 추가
sosow0212 Feb 16, 2023
62e068c
refactor: OutputView 출력 로직 수정
sosow0212 Feb 16, 2023
6e40af6
refactor: OutputView 플레이어 출력 로직 리팩토링
sosow0212 Feb 16, 2023
66105bc
feat: 사다리 구성에 필요한 기호를 반환해주는 Enum 생성
sosow0212 Feb 16, 2023
ff1c578
메서드 분리 작업 및 리네이밍 진행
sosow0212 Feb 16, 2023
e9db312
docs: 기능 구현 목록 최신화
sosow0212 Feb 16, 2023
fe2fedd
refactor: 테스트 메서드 리네이밍 및 테스트 추가
sosow0212 Feb 16, 2023
6821eda
refactor: Ladder 테스트 생성
sosow0212 Feb 17, 2023
d942937
refactor: Ladder 생성자를 필드에 있는 객체로 받게 리팩토링 진행
sosow0212 Feb 17, 2023
220adf4
refactor: 방어적 복사를 통해 반환되는 Collection타입의 값을 외부에서 변경하지 못하게 변경
sosow0212 Feb 17, 2023
53d1cf6
test: 예외 테스트에서, 정상적인 값이 들어온 경우의 케이스도 추가
sosow0212 Feb 17, 2023
ec22043
refactor: 변수명 통일
sosow0212 Feb 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,47 @@
## 우아한테크코스 코드리뷰

- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)

## 기능 요구사항

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

---

## 기능 리스트

- 입력에서 처리해야하는 예외
- [x] height -> 숫자만 가능하도록

- Player(PlayerName)
- 예외
- [x] 이름은 최소 1자 이상, 최대 5글자
- [x] 이름의 길이를 반환한다.

- Players
- 예외
- [x] 참여 가능 플레이어 수는 2명 이상, 10명 이하이다.
- [x] 참여 플레이어의 이름 중 중복된 이름이 있으면 안된다.
- [x] 플레이어들의 이름 중 가장 긴 이름의 길이를 반환해준다.
- [x] 플레이어 만드는 기능
- [x] 참여한 플레이어의 숫자를 반환한다.
- [x] 첫번째 플레이어의 이름, 이름 길이를 반환한다.

- Ladder(Height, Lines)
- Height, Line을 관리한다. (확장성)

- Height(int height)
- 예외
- [x] 높이는 1 이상 10 이하이다.
- [x] 높이는 입력을 받은 만큼 생성된다.

- Line (높이당 1개씩)
- [x] 발판 만드는 기능

- LadderSymbol
- [x] 기호를 반복 횟수만큼 반환하는 기능
9 changes: 9 additions & 0 deletions src/main/java/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import controller.LadderGameController;

public class Application {

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

import domain.Height;
import domain.Ladder;
import domain.Lines;
import domain.Players;
import java.util.List;
import view.InputView;
import view.OutputView;

public class LadderGameController {

private final InputView inputView;
private final OutputView outputView;

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

public void run() {
Players players = makePlayers();
Ladder ladder = makeLadder(players.findNumberOfPlayers());

outputView.printResult(players, ladder);
}

private Players makePlayers() {
try {
List<String> playerNames = inputView.readPlayerNames();
return new Players(playerNames);
} catch (IllegalArgumentException exception) {
System.out.println(exception.getMessage());
return makePlayers();
}
}

private Ladder makeLadder(int numberOfPlayers) {
try {
int ladderHeight = inputView.readHeight();
Ladder ladder = new Ladder(new Lines(numberOfPlayers, ladderHeight), new Height(ladderHeight));
return ladder;
} catch (IllegalArgumentException exception) {
System.out.println(exception.getMessage());
return makeLadder(numberOfPlayers);
}
}
}
28 changes: 28 additions & 0 deletions src/main/java/domain/Height.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package domain;

public class Height {

private static final int MINIMUM_LENGTH_OF_HEIGHT = 1;
private static final int MAXIMUM_LENGTH_OF_HEIGHT = 10;

private final int height;

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

private void validateLengthOfHeight(final int height) {
if (isNotPermittedLengthOfHeight(height)) {
throw new IllegalArgumentException("사다리의 높이는 최소 1이상 최대 10이하입니다.");
}
}

private boolean isNotPermittedLengthOfHeight(int height) {
return (height < MINIMUM_LENGTH_OF_HEIGHT) || (height > MAXIMUM_LENGTH_OF_HEIGHT);
}

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

public class Ladder {

private final Lines lines;
private final Height height;

public Ladder(final Lines lines, final Height height) {
this.lines = lines;
this.height = height;
}

public int findLadderHeight() {
return this.height.getHeight();
}

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

import java.util.Arrays;

public enum LadderSymbol {

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

private final String symbol;

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

public static String draw(final String symbol, final int repeatedNumber) {
return find(symbol).symbol.repeat(repeatedNumber);
}

private static LadderSymbol find(final String symbol) {
return Arrays.stream(values())
.filter(value -> value.symbol.equals(symbol))
.findAny()
.orElseThrow(IllegalArgumentException::new);
}
}
58 changes: 58 additions & 0 deletions src/main/java/domain/Line.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package domain;

import java.util.ArrayList;
import java.util.List;
import utils.FootholdGenerator;

public class Line {

private final List<Boolean> footholds;

public Line(final int numberOfPlayer, final FootholdGenerator footholdGenerator) {
footholds = makeFootholds(numberOfPlayer, footholdGenerator);
}

private List<Boolean> makeFootholds(final int numberOfPlayer, final FootholdGenerator footholdGenerator) {
List<Boolean> footholds = new ArrayList<>();

generateFootholds(footholdGenerator, footholds, numberOfPlayer);

return footholds;
}

private void generateFootholds(final FootholdGenerator footholdGenerator, final List<Boolean> footholds,
final int numberOfPlayer) {
int numberOfPoint = numberOfPlayer - 1;

createFirstFoothold(footholds, footholdGenerator);
createOtherFootholds(footholdGenerator, footholds, numberOfPoint);
}

private void createFirstFoothold(final List<Boolean> points, final FootholdGenerator footholdGenerator) {
points.add(footholdGenerator.generate());
}

private void createOtherFootholds(final FootholdGenerator footholdGenerator, final List<Boolean> footholds,
final int numberOfPoint) {
for (int i = 1; i < numberOfPoint; i++) {
boolean isExistFoothold = footholdGenerator.generate();
createFootholdWithoutFirst(footholds, i, isExistFoothold);
}
}

private void createFootholdWithoutFirst(final List<Boolean> points, final int index, final boolean isExistFoothold) {
if (isConsecutiveExistingFoothold(points, index, isExistFoothold)) {
points.add(false);
return;
}
points.add(isExistFoothold);
}

private boolean isConsecutiveExistingFoothold(final List<Boolean> points, final int index, final boolean random) {
return points.get(index - 1) && random;
}

public List<Boolean> getFootholds() {
return footholds;
}
}
37 changes: 37 additions & 0 deletions src/main/java/domain/Lines.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package domain;

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

public class Lines {

private final List<Line> lines;

public Lines(final int numberOfPlayer, final int height) {
this.lines = makeLines(numberOfPlayer, height);
}

public Lines(final List<Line> lines) {
this.lines = lines;
}

private List<Line> makeLines(final int numberOfPlayer, final int height) {
List<Line> lines = new ArrayList<>();

for (int i = 0; i < height; i++) {
lines.add(new Line(numberOfPlayer, new RandomFootholdGenerator()));
}

return lines;
}

public List<Boolean> findSelectedLine(final int selectedPosition) {
return this.lines.get(selectedPosition).getFootholds();
}

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

public class Player {

private final PlayerName playerName;

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

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

public int getLengthOfPlayerName() {
return this.playerName.getLengthOfName();
}
}
32 changes: 32 additions & 0 deletions src/main/java/domain/PlayerName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package domain;

public class PlayerName {

private static final int MINIMUM_LENGTH_OF_NAME = 1;
private static final int MAXIMUM_LENGTH_OF_NAME = 5;

private final String name;

public PlayerName(final String name) {
validateLengthOfName(name);
this.name = name;
}

private void validateLengthOfName(final String name) {
if (isNotPermittedLengthOfName(name)) {
throw new IllegalArgumentException("이름의 길이는 최소 1자 이상, 5자 이하입니다.");
}
}

private boolean isNotPermittedLengthOfName(final String name) {
return (name.length() < MINIMUM_LENGTH_OF_NAME) || (name.length() > MAXIMUM_LENGTH_OF_NAME);
}

Comment on lines +2 to +24

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.

@sonypark
추가 질문을 어디에 남길지 몰라서 여기에 남깁니다..!

테스트에 대한 질문입니다!
자동차 경주 결과를 출력하는 로직이 있다면, 이를 테스트 하기 위해서 Car 클래스에 move()를 사용해야합니다. 경주 결과는 Cars 클래스에서 결과를 리턴해준다고 가정했을 때,

Cars.경주결과() 를 테스트 하기 위해서 테스트에서 car.move()와 같은 작동을 미리 해줘야합니다.(경주 결과 판단을 위해서) 이런 경우 Cars 테스트Car 도메인 메서드를 의존하게 되는데 이런 경우는 어떻게 하면 좋을까요?

소니의 생각이 궁금합니다.

Choose a reason for hiding this comment

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

네 제이 말대로 Cars가 Car를 가지고 있고 Car의 move 메서드를 통해야 경기 결과를 도출할 수 있습니다.
이건 도메인의 의존성 때문에 테스트에서도 동일한 의존성이 생기는것으로 자연스러운 흐름이라 생각합니다.
레이어별로 controller -> service -> domain -> repository 와 같이 의존성을 갖게 되는데 가장 하위의 레이어의 단위 테스트가 잘 작성되어 있어야 합니다. 하위 레이어의 정상 작동이 보장되어야 그것을 사용하는 상위 레이어도 로직의 안정성을 보장할 수 있습니다. 그렇기 때문에 의존성을 어디에 얼마나 두느냐가 중요한 것 같습니다. 따라서 하위 레이어일수록 꼼꼼히 테스트를 작성합니다. 테스트의 중요도와 우선순위도 (repository) -> domain -> service -> controller 순입니다. (repository는 이미 검증된 라이브러리를 사용하기 때문에 잘 테스트하진 않습니다.🙃)

public int getLengthOfName() {
return this.name.length();
}

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