-
Notifications
You must be signed in to change notification settings - Fork 252
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단계 - 사다리 게임 실행] 몰리(김지민) 미션 제출합니다. #412
Changes from all commits
09eff2d
c3611ac
fc9fbb3
6653ac1
6674645
ad1df2e
eeb57ad
8f7c474
233b959
62b7858
e4b4705
38c0034
8611540
b686b45
e3374fe
6248314
4dd6267
f3c1f14
77dcea7
75a2038
7332308
ef5a04a
8c89ecc
007b784
64f266e
19da1be
fe1bc1f
a023ce8
8bf415d
c683dd0
958d538
a80f45a
64a2af2
7460b56
b9ca5e0
9f73c3e
3897b47
d815880
1b21725
164e846
a178d6a
9ceeb00
6546037
ddcab56
76f9182
e628677
16d1d46
885cfbc
2b087bb
2bcd82e
2fce25f
7afc04e
374e5e4
1725073
7474aa9
467bcdf
3102ec3
005a39d
9aa99fd
3d42144
af02405
050ca08
d040eea
dbfdee2
791c3e1
325186c
5ff4dfe
0beb4be
de2803c
8a88fcb
d735f2e
ee41901
1c6a4ab
73f9fc1
c43a793
224c4e0
7401da5
3b0028c
27a240c
bb3d4e5
c33737a
53c3cf8
6b4b225
0e82e4c
f050e11
a829054
4840e57
e0fddcc
cb14e71
d78b5ba
7b398e4
9b43ed1
b333695
851659a
83617b5
cc598c2
4b78abe
e0d9490
0db785c
f5bfa57
c576b24
1827704
a4e6562
1641858
2982817
ada3c62
7409fd5
477b7c4
c144fd6
ee50f71
00099fb
ad3a889
bad0d60
a66bfa2
5012257
9e16b5d
48e1cfb
8210b69
fa71827
35c4447
8cba210
009f318
b99ed5e
8d34072
3dcc6fc
fed6cf0
9bbb749
94504fd
654de82
0adfd07
2a0a39e
abda7f8
4ac967f
142bcd0
7229cae
ed1e064
699fee2
c6ec2a2
e015ff6
03b629c
f65a0b2
f38831e
1c6337e
2c6d49b
c670c3e
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,19 +1,47 @@ | ||
### 기능 요구사항 정리 | ||
|
||
- [x] 사람의 이름을 갖는다. | ||
- [x] 이름은 최소 1글자 최대 5글자다. | ||
- [x] 이름은 최소 1글자 최대 5글자다. | ||
- [x] 라인은 사다리의 가로 한줄을 의미한다. | ||
- [x] 한 라인은 사람수보다 하나 적은 폭을 갖는다. | ||
- [x] 사다리의 경로(path:가로라인)은 연달아 있을 수 없다. (나란히 있을 수 없다.) | ||
- [x] 한 라인은 사람수보다 하나 적은 폭을 갖는다. | ||
- [x] 사다리의 경로(path:가로라인)은 연달아 있을 수 없다. (나란히 있을 수 없다.) | ||
|
||
- [x] 참여할 사람의 이름은 쉼표로 구분해 받는다. | ||
- [x] 사람은 최소 2명 참가해야한다. | ||
- [x] 실행 결과의 목록을 입력 받는다. | ||
- [x] 실행 결과는 이름을 가진다. | ||
- [x] 실행 결과의 이름은 공백일 수 없다. | ||
- [x] 실행 결과 목록의 각 결과는 결과의 인덱스와 동일한 최종 위치의 사람에게 주어진다. | ||
- [x] 사다리의 높이를 입력받는다. | ||
- [x] 최소 높이는 1 이상이다. | ||
- [x] 실행 결과를 출력한다. | ||
- [x] 사다리를 출력한다. | ||
- [x] 라인의 첫 부분은 ` ` 4칸으로 출력한다. | ||
- [x] 5칸과 가장 오른쪽에 `|`를 높이만큼 출력한다. | ||
- [x] 라인에 경로가 있는 경우 `-`를 5칸을 출력한다. | ||
- [x] 라인에 경로가 없는 경우 ` `를 5칸을 출력한다. | ||
- [x] 이름을 출력한다. | ||
- [x] 이름이 4글자 이하인 경우, 오른쪽에 하나의 공백을 두고 우측 정렬한다. | ||
- [x] 이름이 5글자인 경우, 공백없이 우측 정렬한다. | ||
- [x] 사다리 타기 완료 후, 결과를 저장한다. | ||
- [x] 결과를 보고 싶은 사람을 입력받는다. | ||
- [x] 전체 결과를 보고 싶을 경우 all 을 입력한다. | ||
|
||
- [x] 사다리 결과를 출력한다. | ||
- [x] 사다리를 출력한다. | ||
- [x] 라인의 첫 부분은 ` ` 4칸으로 출력한다. | ||
- [x] 5칸과 가장 오른쪽에 `|`를 높이만큼 출력한다. | ||
- [x] 라인에 경로가 있는 경우 `-`를 5칸을 출력한다. | ||
- [x] 라인에 경로가 없는 경우 ` `를 5칸을 출력한다. | ||
- [x] 이름을 출력한다. | ||
- [x] 이름이 4글자 이하인 경우, 오른쪽에 하나의 공백을 두고 우측 정렬한다. | ||
- [x] 이름이 5글자인 경우, 공백없이 우측 정렬한다. | ||
- [x] 실행 결과를 출력한다. | ||
- [x] 실행 결과는 좌측 정렬하여 출력한다. | ||
- [x] 결과를 보고 싶은 사람에 대한 결과를 출력한다. | ||
- [x] 개인에 대한 결과인 경우, 결과만 출력한다. | ||
- [x] 전체 결과일 경우, :를 기준으로 이름과 결과를 출력한다. | ||
- [x] 전체 결과를 출력한 경우 게임을 종료한다. | ||
|
||
--- | ||
|
||
### 예외처리 | ||
|
||
- [x] 사람의 이름이 1글자 이하, 5글자 초과인 경우 | ||
- [x] 사다리의 높이가 1 이하인 경우 | ||
- [x] 참여할 사람의 이름이 쉼표로 구분되어 있지 않은 경우 | ||
- [ ] 참여한 인원 중 중복인 이름이 존재할 경우 | ||
- [x] 참여한 인원의 수가 1명 이하인 경우 | ||
~~- [ ] 실행 결과 목록이 쉼표로 구분되어 있지 않은 경우~~ | ||
- 쉼표 자체가 포함된 이름이 있을 수 있으므로 유효한 입력으로 취급한다. | ||
- [x] 참여 인원 수와 결과 목록의 수가 동일하지 않은 경우 | ||
- [x] 결과를 보려는 사람이 사다리 참여자에 없는 경우 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,17 @@ | ||
package controller; | ||
|
||
import dto.Result; | ||
import dto.LineDto; | ||
import dto.ResultsDto; | ||
import java.util.List; | ||
import model.Ladder; | ||
import model.People; | ||
import model.path.RandomPathGenerator; | ||
import model.MatchedIndex; | ||
import model.items.Item; | ||
import model.ladder.Height; | ||
import model.items.Items; | ||
import model.ladder.Ladder; | ||
import model.ladder.line.Line; | ||
import model.people.People; | ||
import model.ladder.line.RandomLinesGenerator; | ||
import model.result.Results; | ||
import view.InputView; | ||
import view.OutputView; | ||
|
||
|
@@ -19,20 +26,61 @@ public LadderGame(final InputView inputView, final OutputView outputView) { | |
|
||
public void play() { | ||
final People people = initPeople(); | ||
final Ladder ladder = initLadder(people.getPersonCount()); | ||
final Items items = initItems(people.findPeopleSize()); | ||
final Ladder ladder = initLadder(people.findPeopleSize()); | ||
|
||
final Result result = Result.from(people, ladder); | ||
outputView.printResult(result); | ||
} | ||
printLadder(people, ladder, items); | ||
|
||
final Results results = findResults(people, ladder, items); | ||
searchResult(results); | ||
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. play() 를 전체적으로 읽을 때 어떤 일이 일어나는지 깔끔하게 표현됐네요 👍 |
||
} | ||
|
||
private People initPeople() { | ||
final List<String> names = inputView.inputNames(); | ||
return People.from(names); | ||
final List<String> peopleNames = inputView.inputPeopleNames(); | ||
return People.from(peopleNames); | ||
} | ||
|
||
private Ladder initLadder(final int personCount) { | ||
final int height = inputView.inputHeight(); | ||
return Ladder.from(height, personCount, new RandomPathGenerator()); | ||
final Height height = new Height(inputView.inputHeight()); | ||
return Ladder.from(height, personCount, new RandomLinesGenerator()); | ||
} | ||
|
||
private Items initItems(final int personCount) { | ||
List<String> itemNames = inputView.inputItemsNames(); | ||
return Items.of(itemNames, personCount); | ||
} | ||
|
||
private void printLadder(final People people, final Ladder ladder, final Items items) { | ||
outputView.printLadderResultDescription(); | ||
outputView.printPeopleNames(people.getNames()); | ||
printLadderFormat(ladder); | ||
outputView.printItemNames(items.getNames()); | ||
} | ||
|
||
private void printLadderFormat(final Ladder ladder) { | ||
List<Line> lines = ladder.getLines(); | ||
List<LineDto> lineDtos = lines.stream() | ||
.map(LineDto::from) | ||
.toList(); | ||
|
||
outputView.printLineDtos(lineDtos); | ||
} | ||
|
||
private Results findResults(final People people, final Ladder ladder, final Items items) { | ||
List<MatchedIndex> resultIndexes = ladder.climbAll(); | ||
return Results.of(people, resultIndexes, items); | ||
} | ||
|
||
private void searchResult(final Results results) { | ||
String personName = inputView.inputPersonName(); | ||
|
||
while (!inputView.isEnd()) { | ||
final Item item = results.findItemByPerson(personName); | ||
outputView.printResultByPerson(item.getName()); | ||
personName = inputView.inputPersonName(); | ||
} | ||
|
||
final ResultsDto resultsDto = ResultsDto.from(results); | ||
outputView.printAllResult(resultsDto); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package dto; | ||
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. dto 는 사실 view나 controller 에서 사용하고 있으니
어느곳이 더 어울리는지는 몰리의 고민거리로 남겨두겠습니다 😄 |
||
|
||
import java.util.List; | ||
import model.ladder.line.Line; | ||
|
||
public record LineDto(List<Boolean> lineInfo) { | ||
public static LineDto from(final Line line) { | ||
final List<Boolean> lineInfo = line.getExistFlags(); | ||
return new LineDto(lineInfo); | ||
} | ||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package dto; | ||
|
||
import model.items.Item; | ||
import model.people.Person; | ||
import model.result.Result; | ||
|
||
public record ResultDto(String personName, String itemName) { | ||
public static ResultDto from(final Result result) { | ||
Item item = result.getItem(); | ||
Person person = result.getPerson(); | ||
return new ResultDto(person.getName(), item.getName()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package dto; | ||
|
||
import java.util.List; | ||
import model.result.Results; | ||
|
||
public record ResultsDto(List<ResultDto> personAndItemName) { | ||
public static ResultsDto from(final Results results) { | ||
List<ResultDto> resultDtos = results.getResults() | ||
.stream() | ||
.map(ResultDto::from) | ||
.toList(); | ||
return new ResultsDto(resultDtos); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package model; | ||
|
||
public abstract class Count { | ||
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. Count 를 추상 클래스로 만들어주신 이유가 궁금해요! Count 를 구현한 PersonCount와 ItemCount가 Count 라는 개념으로 묶였을 때 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. 카운트는 음수일 수 없다는 제약조건을 강제하고 각 Count에 맞는 검증을 추가할 수 있도록 하기 위해 추상 클래스로 구현했습니다!
코드상에서는 PersonCount와 ItemCount가 Count의 한 종류이다! 라는 관계가 표현될 것 같아요 추가로 Countable이라는 인터페이스로 구현하려면, (검증 코드를 제외하고) Countable이라는 특성과 관련하여 사용될 행동이 있어야 한다고 생각해요. 그런데 제 코드에는 Countable의 특성에 맞는 필요한, 공통된 행동이 없다고 판단했어요. 그래서 검증 코드의 재사용을 위해 추상 클래스로 구현해도 괜찮을 것이라 생각했는데, 혹시 사용이 어색한지 찰리에게 묻고 싶어요! 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. 저는 추상화를 했다면 Count 타입으로 사용했을 때 어색함이 없어야한다고 생각해요! 그런 측면에서 ItemCount 와 PeopleCount이 동일한 추상화를 바라보는 객체인지 고민이 필요해보여요! 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. 더 이상 사용하지 않는 Count 클래스는 삭제해도 되겠군요 😉 |
||
private static final int MINIMUM_NUMBER = 0; | ||
|
||
private final int value; | ||
|
||
protected Count(final int rawCount) { | ||
validateCount(rawCount); | ||
validateNegativeCount(rawCount); | ||
this.value = rawCount; | ||
} | ||
|
||
protected abstract void validateCount(final int rawCount); | ||
|
||
private void validateNegativeCount(final int rawCount) { | ||
if (rawCount < MINIMUM_NUMBER) { | ||
throw new IllegalArgumentException("카운트는 음수일 수 없습니다."); | ||
} | ||
} | ||
|
||
public int getCount() { | ||
return value; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package model; | ||
|
||
import java.util.Objects; | ||
|
||
public class Index { | ||
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. 장점으로는 해당 값에 대한 검증을 포장한 객체 안에서 수행할 수 있다는 것 같아요..! 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. 처음 의문처럼 너무 작은것까지 원시값 포장을 진행한 것은 아닌지 의심이 됐어요 😃 name이라는 String을 포장한 객체가 Person이 아니었을까요! 🤔 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. 맞아요..! |
||
private static final int MINIMUM_NUMBER = 0; | ||
|
||
private final int value; | ||
|
||
public Index(final int rawIndex) { | ||
validateNegativeIndex(rawIndex); | ||
this.value = rawIndex; | ||
} | ||
|
||
private void validateNegativeIndex(final int rawCount) { | ||
if (rawCount < MINIMUM_NUMBER) { | ||
throw new IllegalArgumentException("인덱스는 음수일 수 없습니다."); | ||
} | ||
} | ||
|
||
public boolean isNotLastIndex(int last) { | ||
return value < last; | ||
} | ||
|
||
public boolean isNotStartIndex() { | ||
return value != 0; | ||
} | ||
|
||
@Override | ||
public boolean equals(final Object o) { | ||
if (this == o) { | ||
return true; | ||
} | ||
if (o == null || getClass() != o.getClass()) { | ||
return false; | ||
} | ||
Index index = (Index) o; | ||
return value == index.value; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(value); | ||
} | ||
|
||
public int getIndex() { | ||
return value; | ||
} | ||
|
||
public Index getNext() { | ||
return new Index(value + 1); | ||
} | ||
|
||
public Index getPast() { | ||
return new Index(value - 1); | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package model; | ||
|
||
|
||
public record MatchedIndex(Index startIndex, Index finalIndex) { | ||
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. MatchedIndex 명확한 이름이군요! 😃 |
||
} |
This file was deleted.
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.
TDD cycle을 잘 지키는 방법을 고민해봤는데 의식적인 연습 외에 생각나는 것이 없군요..!
자신도 모르게 기능 코드를 작성하는 때가 있는데
이 때 작성했던 코드를 지우고 다시 테스트 부터 작성하는 연습도 시도해볼 수 있겠군요 😄
이건 우테코처럼 교육 기간이나 개인적인 시간이 허락할 때 가능한 이야기이니 지금 시도해봐도 좋을것같아요~!
결론 : TDD 한다는 것을 계속 의식하면서 구현하는 연습을 해야합니다!
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.
역시 의식적인 연습이 중요하군요! 다음 TDD 때는 더 의식적으로 TDD를 인지하면서 구현을 해봐야겠어요!
감사합니다 찰리:)