-
Notifications
You must be signed in to change notification settings - Fork 251
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단계 - 사다리 생성] 에버(손채영) 미션 제출합니다. #316
Changes from all commits
88b71e0
63efb03
d6d3437
b9ae327
50bb66e
dba6496
2460723
df6505e
dbb5bf6
d862bce
960163e
7f82f81
afd6d85
6f309ff
b902809
63aff0e
6ed33af
7a2b7f4
b9fc1f8
8fd9449
e8d008c
416a256
1215174
e198c19
bd00b5d
4cd2c8c
fec15a1
2917f27
8bd7344
a8b502e
fbe8f4e
2a3966e
e5de9eb
7b92aa4
8546fb2
b61e163
39397ee
fc00918
829f6be
90f7b30
8052c65
0c478c6
9dd66ca
7c79b51
bb9e0cc
f22fe8a
057fd7a
44fa207
1a91b97
d4efe94
c4e12b4
24a26bd
011baed
84619c4
cb3e036
fdff455
5183c25
74b7287
25dbd20
7abb36c
a076bae
c4ae098
332b757
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,25 @@ | ||
# java-ladder | ||
## 기능 요구사항 | ||
### 참여자 | ||
- [x] 이름은 1글자부터 5글자까지 부여할 수 있다. | ||
- [x] 이름은 쉼표(,)를 기준으로 구분한다. | ||
- [x] 이름은 중복될 수 없다. | ||
- [x] 이름은 알파벳 대소문자 + 숫자로 구성되어야 한다. | ||
- [x] 참여자 수는 최소 2명부터 최대 15명까지이다. | ||
|
||
사다리 타기 미션 저장소 | ||
### 사다리 | ||
- [x] 사다리 가로줄의 생성 여부는 랜덤으로 결정된다. | ||
- [x] 사다리 가로줄 라인이 겹치지 않도록 해야 한다. | ||
- [x] `|-----|-----|` 와 같은 모양은 허용하지 않는다. | ||
- [x] 사다리 높이는 1부터 20까지 가능하다. | ||
|
||
## 우아한테크코스 코드리뷰 | ||
### 입력 | ||
- [x] 참여할 사람의 이름을 쉼표(,)로 구분하여 입력받는다. | ||
- [x] 최대 사다리 높이를 입력받는다. | ||
- [x] 잘못된 형태로 입력된 경우 다시 입력받는다. | ||
|
||
- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md) | ||
### 출력 | ||
- [x] 사람 이름이 출력되고, 그 다음줄부터 사다리가 출력된다. | ||
- [x] 사람 이름이 5자 미만인 경우 이름의 마지막 글자가 사다리 세로줄의 바로 왼쪽 열에 위치하게 한다. | ||
- [x] 사람 이름이 5자인 경우 이름의 마지막 글자가 사다리 세로줄과 같은 열에 위치하게 한다. | ||
- [x] 사다리 각 행의 가장 왼쪽에는 4칸의 공백이 있다. | ||
- [x] 사다리 세로줄 간의 간격은 5칸으로 한다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import controller.GameController; | ||
import error.ErrorHandler; | ||
import view.InputView; | ||
import view.OutputView; | ||
|
||
public class Main { | ||
|
||
public static void main(String[] args) { | ||
InputView inputView = new InputView(); | ||
OutputView outputView = new OutputView(); | ||
ErrorHandler errorHandler = new ErrorHandler(); | ||
|
||
GameController gameController = new GameController(inputView, outputView, errorHandler); | ||
gameController.run(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package controller; | ||
|
||
import domain.Game; | ||
import domain.Lines; | ||
import domain.Members; | ||
import domain.StringParser; | ||
import error.ErrorHandler; | ||
import strategy.RandomConnectionStrategy; | ||
import view.InputView; | ||
import view.OutputView; | ||
|
||
public class GameController { | ||
|
||
private final InputView inputView; | ||
private final OutputView outputView; | ||
private final ErrorHandler errorHandler; | ||
|
||
public GameController(InputView inputView, OutputView outputView, ErrorHandler errorHandler) { | ||
this.inputView = inputView; | ||
this.outputView = outputView; | ||
this.errorHandler = errorHandler; | ||
} | ||
|
||
public void run() { | ||
|
||
Members members = makeMembers(); | ||
|
||
Lines lines = makeLines(members); | ||
|
||
Game game = new Game(members, lines); | ||
outputView.printResult(game); | ||
} | ||
|
||
private Members makeMembers() { | ||
return errorHandler.readUntilNoError(() -> { | ||
String rawNames = inputView.readNames(); | ||
return Members.from(rawNames); | ||
}); | ||
} | ||
|
||
private Lines makeLines(Members members) { | ||
return errorHandler.readUntilNoError(() -> { | ||
String rawHeight = inputView.readHeight(); | ||
int height = StringParser.stringToInt(rawHeight); | ||
return Lines.of(members.getCount(), height, new RandomConnectionStrategy()); | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package domain; | ||
|
||
import strategy.ConnectionStrategy; | ||
|
||
public enum Connection { | ||
|
||
CONNECTED, | ||
DISCONNECTED; | ||
|
||
Connection() { | ||
} | ||
|
||
public Connection makeNextConnection(ConnectionStrategy connectionStrategy) { | ||
if (this == CONNECTED) { | ||
return DISCONNECTED; | ||
} | ||
return connectionStrategy.generateConnection(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package domain; | ||
|
||
public class Game { | ||
|
||
private final Members members; | ||
private final Lines lines; | ||
|
||
public Game(Members members, Lines lines) { | ||
this.members = members; | ||
this.lines = lines; | ||
} | ||
|
||
public Members getMembers() { | ||
return members; | ||
} | ||
|
||
public Lines getLines() { | ||
return lines; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package domain; | ||
|
||
public class Height { | ||
|
||
Comment on lines
+3
to
+4
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 테스트에서만 사용하는 클래스가 되었네요! 이제 안쓰는 클래스일까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 리팩토링하는 과정에서 Height 클래스 내의 로직들을 외부로 넘기다보니 무의미한 클래스가 되어버렸네요... |
||
public static final int MIN_HEIGHT = 1; | ||
public static final int MAX_HEIGHT = 20; | ||
|
||
private final int value; | ||
|
||
public Height(int value) { | ||
validateRange(value); | ||
this.value = value; | ||
} | ||
|
||
private void validateRange(int value) { | ||
if (value < MIN_HEIGHT || value > MAX_HEIGHT) { | ||
throw new IllegalArgumentException(MIN_HEIGHT + " 이상 " + MAX_HEIGHT + " 이하의 숫자를 입력해 주세요."); | ||
} | ||
} | ||
|
||
public int getValue() { | ||
return value; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package domain; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import strategy.ConnectionStrategy; | ||
|
||
public class Line { | ||
|
||
private final List<Connection> connections; | ||
|
||
private Line(List<Connection> connections) { | ||
this.connections = connections; | ||
} | ||
|
||
public static Line from(int memberCount, ConnectionStrategy connectionStrategy) { | ||
List<Connection> connections = new ArrayList<>(); | ||
connections.add(connectionStrategy.generateConnection()); | ||
|
||
for (int i = 1; i < memberCount - 1; i++) { | ||
Connection previous = connections.get(i - 1); | ||
Connection next = previous.makeNextConnection(connectionStrategy); | ||
connections.add(next); | ||
} | ||
return new Line(connections); | ||
} | ||
|
||
public List<Connection> getConnections() { | ||
return connections; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package domain; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import strategy.ConnectionStrategy; | ||
|
||
public class Lines { | ||
|
||
private final List<Line> lines; | ||
|
||
private Lines(List<Line> lines) { | ||
this.lines = lines; | ||
} | ||
|
||
public static Lines of(int memberCount, int height, ConnectionStrategy connectionStrategy) { | ||
List<Line> lines = new ArrayList<>(); | ||
for (int i = 0; i < height; i++) { | ||
lines.add(Line.from(memberCount, connectionStrategy)); | ||
} | ||
return new Lines(lines); | ||
} | ||
|
||
public List<Line> getLines() { | ||
return Collections.unmodifiableList(lines); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package domain; | ||
|
||
public class Member { | ||
|
||
private final Name name; | ||
|
||
public Member(String rawName) { | ||
this.name = new Name(rawName); | ||
} | ||
|
||
public String getName() { | ||
return name.getName(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,56 @@ | ||||||||||||||||||||||
package domain; | ||||||||||||||||||||||
|
||||||||||||||||||||||
import java.util.ArrayList; | ||||||||||||||||||||||
import java.util.HashSet; | ||||||||||||||||||||||
import java.util.List; | ||||||||||||||||||||||
import java.util.Set; | ||||||||||||||||||||||
|
||||||||||||||||||||||
public class Members { | ||||||||||||||||||||||
|
||||||||||||||||||||||
private static final String DELIMITER = ","; | ||||||||||||||||||||||
private static final int MIN_MEMBER_COUNT = 2; | ||||||||||||||||||||||
private static final int MAX_MEMBER_COUNT = 15; | ||||||||||||||||||||||
|
||||||||||||||||||||||
private final List<Member> members; | ||||||||||||||||||||||
|
||||||||||||||||||||||
private Members(List<Member> members) { | ||||||||||||||||||||||
this.members = members; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
public static Members from(String rawNames) { | ||||||||||||||||||||||
List<String> names = StringParser.splitByDelimiter(rawNames, DELIMITER); | ||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 객체의 역할이 맞을까요? input이 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 헉 그러네요. 구분자가 변경될 경우 Members 클래스가 수정되어야하는 상황이 발생하네요. |
||||||||||||||||||||||
validateDuplication(names); | ||||||||||||||||||||||
validateCount(names); | ||||||||||||||||||||||
List<Member> members = makeMembers(names); | ||||||||||||||||||||||
return new Members(members); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
private static void validateDuplication(List<String> names) { | ||||||||||||||||||||||
Set<String> nonDuplicated = new HashSet<>(names); | ||||||||||||||||||||||
if (names.size() != nonDuplicated.size()) { | ||||||||||||||||||||||
throw new IllegalArgumentException("이름은 서로 중복될 수 없습니다."); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
private static void validateCount(List<String> names) { | ||||||||||||||||||||||
if (names.size() < MIN_MEMBER_COUNT || names.size() > MAX_MEMBER_COUNT) { | ||||||||||||||||||||||
throw new IllegalArgumentException("참여자는 " + MIN_MEMBER_COUNT + "~" + MAX_MEMBER_COUNT + "명만 허용됩니다."); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
private static List<Member> makeMembers(List<String> names) { | ||||||||||||||||||||||
List<Member> members = new ArrayList<>(); | ||||||||||||||||||||||
names.forEach(name -> members.add(new Member(name))); | ||||||||||||||||||||||
return members; | ||||||||||||||||||||||
} | ||||||||||||||||||||||
Comment on lines
+41
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||
|
||||||||||||||||||||||
public int getCount() { | ||||||||||||||||||||||
return members.size(); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
|
||||||||||||||||||||||
public List<String> getNames() { | ||||||||||||||||||||||
return members.stream() | ||||||||||||||||||||||
.map(Member::getName) | ||||||||||||||||||||||
.toList(); | ||||||||||||||||||||||
} | ||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package domain; | ||
|
||
public class Name { | ||
|
||
private static final int MIN_NAME_LENGTH = 1; | ||
private static final int MAX_NAME_LENGTH = 5; | ||
private static final String FORMAT_NAME = "^[A-Za-z0-9]+$"; | ||
|
||
private final String name; | ||
|
||
public Name(String name) { | ||
validate(name); | ||
this.name = name; | ||
} | ||
|
||
private void validate(String name) { | ||
validateNull(name); | ||
validateLength(name); | ||
validatePattern(name); | ||
} | ||
|
||
private void validateNull(String name) { | ||
if (name == null) { | ||
throw new IllegalArgumentException("이름에 null을 입력할 수 없습니다."); | ||
} | ||
} | ||
|
||
private void validateLength(String name) { | ||
if (name.length() < MIN_NAME_LENGTH || name.length() > MAX_NAME_LENGTH) { | ||
throw new IllegalArgumentException(MIN_NAME_LENGTH + "~" + MAX_NAME_LENGTH + "자의 이름만 허용합니다."); | ||
} | ||
} | ||
|
||
private void validatePattern(String name) { | ||
if (!name.matches(FORMAT_NAME)) { | ||
throw new IllegalArgumentException("이름은 알파벳과 숫자만 허용합니다."); | ||
} | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package domain; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
public class StringParser { | ||
|
||
public static List<String> splitByDelimiter(String before, String delimiter) { | ||
try { | ||
return Arrays.stream(before.split(delimiter, -1)) | ||
.map(String::trim) | ||
.toList(); | ||
} catch (NullPointerException e) { | ||
throw new IllegalArgumentException("null을 입력할 수 없습니다."); | ||
} | ||
} | ||
|
||
public static int stringToInt(String before) { | ||
try { | ||
return Integer.parseInt(before); | ||
} catch (NumberFormatException e) { | ||
throw new IllegalArgumentException("숫자를 입력해 주세요."); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package error; | ||
|
||
import java.util.function.Supplier; | ||
|
||
public class ErrorHandler { | ||
|
||
private static final String ERROR_PREFIX = "[ERROR] "; | ||
|
||
public <T> T readUntilNoError(Supplier<T> supplier) { | ||
try { | ||
return supplier.get(); | ||
} catch (IllegalArgumentException e) { | ||
System.out.println(ERROR_PREFIX + e.getMessage()); | ||
return readUntilNoError(supplier); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package strategy; | ||
|
||
import domain.Connection; | ||
|
||
public interface ConnectionStrategy { | ||
|
||
Connection generateConnection(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
입력 + Members 생성까지 해서 너무 많은 역할을 한다고 피드백 드렸었는데, 이번엔 메소드 역할에 에러 핸들링까지 추가되었어요!
어떻게 변경해보면 좋을까요?