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

Step2 #1830

Open
wants to merge 8 commits into
base: devcat36
Choose a base branch
from
Open

Step2 #1830

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
5 changes: 5 additions & 0 deletions src/main/java/nextstep/ladder/GenerationStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package nextstep.ladder;

public interface GenerationStrategy {
boolean shouldPlace();
}
13 changes: 13 additions & 0 deletions src/main/java/nextstep/ladder/Ladder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package nextstep.ladder;

public class Ladder {
public final Natural height;
public final Users users;
public final Legs legs;

public Ladder(Users users, Natural height, GenerationStrategy strategy) {
this.height = height;
this.users = users;
legs = new Legs(height, users.size(), strategy);
}
}
12 changes: 12 additions & 0 deletions src/main/java/nextstep/ladder/LadderController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package nextstep.ladder;

public class LadderController {
private static final LadderView view = new LadderView();
public static void main(String[] args) {
var users = view.getUsers();
var height = view.getHeight();

var ladder = new Ladder(users, height, new RandomGenerationStrategy());
view.printResults(ladder);
}
}
65 changes: 65 additions & 0 deletions src/main/java/nextstep/ladder/LadderView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package nextstep.ladder;
Copy link

Choose a reason for hiding this comment

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

도메인과 ui 의 패키지를 분리해보는것이 어떨까요 ?


import java.util.Scanner;

public class LadderView {
Copy link

Choose a reason for hiding this comment

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

Input / Output 클래스를 분리하는것도 좋아보입니다 😄

public Users getUsers() {
System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)");
var userInput = new UsersInput(getConsoleInput());
return userInput.toUsers();
}

public Natural getHeight() {
System.out.println("최대 사다리 높이는 몇 개인가요?");
var naturalInput = new NaturalInput(getConsoleInput());
return naturalInput.toNatural();
}

public void printResults(Ladder ladder) {
System.out.println("실행결과");

ladder.users.forEach(user -> System.out.printf("%s\t", user));
printNewLine();
for (var level = ladder.height.value() - 1; level >= 0; level--) {
printLevel(level, ladder);
}
}

private static void printLevel(long level, Ladder ladder) {
for (var place = 0; place < ladder.users.size().value() - 1; place++) {
printColumn();
printLegIfExistsInLadder(new Position(level, place), ladder);
}
printColumn();
printNewLine();
}

private static void printNewLine() {
System.out.println();
}

private static void printColumn() {
System.out.print("|");
}

private static void printLegIfExistsInLadder(Position position, Ladder ladder) {
if (ladder.legs.hasLegOnRightSideOf(position)) {
printFilledLeg();
return;
}
printEmptyLeg();
}

private static void printEmptyLeg() {
System.out.print(" ");
}

private static void printFilledLeg() {
System.out.print("-----");
}

private String getConsoleInput() {
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
}
45 changes: 45 additions & 0 deletions src/main/java/nextstep/ladder/Legs.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package nextstep.ladder;

import java.util.HashSet;
import java.util.Set;
import java.util.stream.LongStream;

public class Legs {
private final Set<Position> legLeftPositions;
Copy link

Choose a reason for hiding this comment

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

Legs일급 컬렉션이 사다리 연결선의 정보를 가지는 Position을 가지는것 보다 Leg라는 클래스를 도출하여 사다리 가로 한줄과 연결선에 대한 정보를 가지는 Position의 구현은 어떻게 생각하시나요 ?
Leg 객체에 사다리를 그릴수 있는지에 대한 메시지를 던져보는 구현도 좋은 구현이 될수 있을것 같습니다 😄


public Legs(Natural height, Natural width, GenerationStrategy strategy) {
legLeftPositions = new HashSet<>();
placeLegs(height, width, strategy);
}

private void placeLegs(Natural height, Natural width, GenerationStrategy strategy) {
LongStream.range(0, height.value())
.mapToObj(Natural::new)
.forEach(level -> placeLegsInLevel(level, width, strategy));
}

private void placeLegsInLevel(Natural level, Natural width, GenerationStrategy strategy) {
LongStream.range(0, width.value() - 1)
.mapToObj(Natural::new)
.map(place -> new Position(level, place))
.forEach(position -> placeLegWithStrategy(strategy, position));
}

private void placeLegWithStrategy(GenerationStrategy strategy, Position position) {
if (isLegPlaceableOnPosition(position) && strategy.shouldPlace()) {
Copy link

Choose a reason for hiding this comment

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

strategy는 매번 random boolean 값을 리턴하고, 현재 위치 기준으로 왼쪽 사다리 연결 선이 존재하는지 검증 👍

legLeftPositions.add(position);
}
}

private boolean isLegPlaceableOnPosition(Position position) {
return isOnTheLeftEdge(position) || !hasLegOnRightSideOf(position.getLeftPosition());
}

private boolean isOnTheLeftEdge(Position position) {
return position.place.value() == 0;
}

public boolean hasLegOnRightSideOf(Position position) {
return legLeftPositions.contains(position);
}
}
39 changes: 39 additions & 0 deletions src/main/java/nextstep/ladder/Natural.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package nextstep.ladder;

public class Natural implements Comparable<Natural> {
private final Long value;

public Natural(long value) {
if (value < 0) {
throw new IllegalArgumentException("Negative value given for natural number");
}
this.value = value;
}

public Long value() {
return value;
}

@Override
public boolean equals(Object o) {
if (o instanceof Natural) {
return value.equals(((Natural) o).value);
}
return false;
}

@Override
public int hashCode() {
return value.hashCode();
}

@Override
public String toString() {
return value.toString();
}

@Override
public int compareTo(Natural o) {
return value.compareTo(o.value);
}
}
20 changes: 20 additions & 0 deletions src/main/java/nextstep/ladder/NaturalInput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package nextstep.ladder;

public class NaturalInput {
Copy link

Choose a reason for hiding this comment

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

NaturalInput을 활용하여 Natural객체를 생성하는것보단 Natural에 다양한 생성자를 정의해보는건 어떨까요 ?

private final String input;

public NaturalInput(String input) {
this.input = input;
checkInputIsNaturalNumber();
}

public Natural toNatural() {
return new Natural(Long.parseLong(input));
}

private void checkInputIsNaturalNumber() {
if (!input.matches("\\d+")) {
throw new IllegalArgumentException("Input is not a natural number");
}
}
}
36 changes: 36 additions & 0 deletions src/main/java/nextstep/ladder/Position.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package nextstep.ladder;

import java.util.Objects;

public class Position {
public final Natural level;
public final Natural place;

public Position(Natural level, Natural place) {
this.level = level;
this.place = place;
}

public Position(long level, long place) {
this.level = new Natural(level);
this.place = new Natural(place);
}

public Position getLeftPosition() {
return new Position(level.value(), place.value() - 1);
}

@Override
public boolean equals(Object o) {
if (o instanceof Position) {
var that = (Position) o;
return level.equals(that.level) && place.equals(that.place);
}
return false;
}

@Override
public int hashCode() {
return Objects.hash(level, place);
}
}
11 changes: 11 additions & 0 deletions src/main/java/nextstep/ladder/RandomGenerationStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package nextstep.ladder;

import java.util.Random;

public class RandomGenerationStrategy implements GenerationStrategy {
private final Random random = new Random();
@Override
public boolean shouldPlace() {
return random.nextBoolean();
}
}
21 changes: 21 additions & 0 deletions src/main/java/nextstep/ladder/Users.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package nextstep.ladder;

import java.util.Iterator;
import java.util.List;

public class Users implements Iterable<String> {
Copy link

Choose a reason for hiding this comment

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

사다리 게임에 참여하는 유저가 1명 이하인경우 정상적인 게임이 동작할수 있을까요 ?

private final List<String> users;

public Users(List<String> users) {
this.users = users;
}

@Override
public Iterator<String> iterator() {
return users.iterator();
}

public Natural size() {
return new Natural(users.size());
}
}
15 changes: 15 additions & 0 deletions src/main/java/nextstep/ladder/UsersInput.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package nextstep.ladder;

import java.util.Arrays;

public class UsersInput {
Copy link

Choose a reason for hiding this comment

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

NatualInput 코멘트와 동일합니다 😄

private final String input;

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

public Users toUsers() {
return new Users(Arrays.asList(input.replace(" ", "").split(",")));
}
}
52 changes: 52 additions & 0 deletions src/test/java/nextstep/ladder/LadderTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package nextstep.ladder;

import org.junit.jupiter.api.Test;

import java.util.List;

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

public class LadderTest {
@Test
Copy link

Choose a reason for hiding this comment

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

@DisplayName 을 활용하여 테스트의 의도를 명확하게 나타내보는건 어떠세요 ?

public void levelAndUsersAreSet() {
var levels = new Natural(5);
var users = new Users(List.of("erik", "jake", "david"));

var ladder = new Ladder(users, levels, () -> true);

assertThat(ladder.height).isEqualTo(levels);
assertThat(ladder.users).containsExactly("erik", "jake", "david");
}

@Test
public void throwErrorOnNegativeInputs() {
assertThatThrownBy(() -> {
var levels = new Natural(-1);
var users = new Users(List.of());
new Ladder(users, levels, () -> true);
}).isInstanceOf(IllegalArgumentException.class)
.hasMessage("Negative value given for natural number");
}

@Test
public void legsAreNotPlacedConsecutively() {
var height = new Natural(5);
var users = new Users(List.of("erik", "jake", "david"));

var ladder = new Ladder(users, height, () -> true);

for (int level = 0; level < height.value(); level++) {
assertThatLegsAreNotPlacedConsecutivelyInLevel(ladder, level, users.size().value());
}
}

private static void assertThatLegsAreNotPlacedConsecutivelyInLevel(Ladder ladder, long level, long width) {
for (int place = 1; place < width; place++) {
var currentPosition = new Position(level, place);
var currentLeg = ladder.legs.hasLegOnRightSideOf(currentPosition);
var leftLeg = ladder.legs.hasLegOnRightSideOf(currentPosition.getLeftPosition());
assert !(leftLeg && currentLeg);
}
}
}
27 changes: 27 additions & 0 deletions src/test/java/nextstep/ladder/NaturalInputTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package nextstep.ladder;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

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

public class NaturalInputTest {
@ParameterizedTest
@ValueSource(strings = {"12wr23", " "})
public void throwErrorOnInvalidInputs(String input) {
assertThatThrownBy(() -> new NaturalInput(input))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("Input is not a natural number");
}

@Test
public void convertsInputToNatural() {
var input = "5";

var natural = new NaturalInput(input).toNatural();

assertThat(natural.value()).isEqualTo(5L);
}
}
16 changes: 16 additions & 0 deletions src/test/java/nextstep/ladder/UsersInputTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package nextstep.ladder;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;

public class UsersInputTest {
@Test
public void parseCommaSplicedUserInput() {
var input = "pobi, honux, crong, jk";

var userInput = new UsersInput(input);

assertThat(userInput.toUsers()).containsExactly("pobi", "honux", "crong", "jk");
}
}