-
Notifications
You must be signed in to change notification settings - Fork 60
[사다리 미션] 정상희 미션 제출합니다. #19
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
base: sangheejeong
Are you sure you want to change the base?
Changes from 42 commits
1196afa
8b96693
f20be9f
19995ef
49d105c
090cb0f
06a00a7
3e47a2a
07fe975
2265787
2fef17c
f7ab094
dbbb328
65d58d2
56b093f
5c6b21e
67fcc5a
74f098d
9763754
a16726a
69871ca
169ec00
ea543eb
e81fa34
047d483
2dfe464
42cdbd6
62f451d
422af42
494e2d9
b2b1220
7dbb8cd
67b09e4
5c280c4
8eac61b
beeb626
570c4d7
6f6b09a
95f8ec1
f31cacc
e43ee2d
7e4cd52
f8dce70
ac02d9c
d48bce2
ea12de3
01b8efe
7cf78a6
174ff32
6b26030
1d96fb1
0bd1928
76e1d31
6473cca
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 |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # 기능 목록 | ||
|
|
||
| ### 1. 플레이어 및 사다리 정보 입력 기능 | ||
| + 플레이어 이름과 결과 종류를 입력한다. | ||
| + 사다리 넓이 : (플레이어 수 - 1) | ||
| + 사다리 높이 : 사용자 입력 | ||
| ### 2. 사다리 실행 기능 | ||
| + |-----|-----| 가로 라인이 겹치지 않게 사다리를 생성한다. | ||
| => 즉, 앞 라인이 true면 다음 라인은 무조건 false이다. | ||
| ### 3. 사다리 출력 기능 | ||
| + 플레이어 이름 입력 순으로 출력 | ||
| + 생성된 사다리 출력 | ||
| + 결과 종류 입력 순으로 출력 | ||
| + 보고 싶은 실행 결과 출력 ("all"일 때는 모두 출력) | ||
|
|
||
| ### <사다리 구현> | ||
| + Boolean (다리) | ||
| + ture : 연결 | ||
| + false : 연결 X | ||
| + List<Boolean> (한 층의 다리 모음) | ||
| + 사다리의 넒이만큼 | ||
| + 다리가 연결되어 있으면(=true) 수평 방향으로 이동이 가능하다. | ||
| + List<Line> (모든 층의 다리 모음) | ||
| + 사다리의 높이만큼 | ||
|
Comment on lines
+1
to
+24
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. [Comment] 제가 코드를 잘 이해할 수 있도록 리드미에 의도를 잘 담아주셔서 감사합니다.😄 이 리드미는 훌륭한 가치와 내용을 담은 리드미라고 생각해요. 기능을 명세화할 떄 여러가지 방법 있겠죠?
이런 측면에서 잘 구조화 해주신 것 같습니다. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import controller.LadderController; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| LadderController ladderController = new LadderController(); | ||
| ladderController.run(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,50 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| package controller; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| import domain.Ladder; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import domain.LadderGenerator; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import domain.Players; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import domain.ResultTypes; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import view.InputView; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import view.OutputView; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| public class LadderController { | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| public void run() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| List<String> playerNames = InputView.splitString(InputView.inputNames()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| List<String> kindOfResults = InputView.splitString(InputView.inputLadderResults()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| // 플레이어 생성 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Players players = new Players(playerNames); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // 결과 종류 생성 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ResultTypes resultTypes = new ResultTypes(kindOfResults, players.getPlayersSize()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // 사다리 초기화 | ||||||||||||||||||||||||||||||||||||||||||||||||||
| Ladder ladder = initializeLadder(players); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| List<String> playerNames = InputView.splitString(InputView.inputNames()); | |
| List<String> kindOfResults = InputView.splitString(InputView.inputLadderResults()); | |
| // 플레이어 생성 | |
| Players players = new Players(playerNames); | |
| // 결과 종류 생성 | |
| ResultTypes resultTypes = new ResultTypes(kindOfResults, players.getPlayersSize()); | |
| // 사다리 초기화 | |
| Ladder ladder = initializeLadder(players); | |
| @PostMapping("/api/ladders") | |
| public ResponseEntity<Void> createLadder(@RequestBody LadderSaveRequest request) { | |
| // 요청 | |
| List<String> playerNames = request.playerNames; | |
| List<String> kindOfResults = request.kindOfResults; | |
| // 비즈니스 로직 호출 | |
| Players players = new Players(playerNames); | |
| ResultTypes resultTypes = new ResultTypes(kindOfResults, players.getPlayersSize()); | |
| Ladder ladder = initializeLadder(players); // 메모리에 저장하거나, 영속화 | |
| // (추가) 응답 생성 | |
| return ResponseEntity.create(이 레더를 찾아갈수있는 경로) | |
| } |
당연히 이게 정답이라고 공감하실 필요도 없고, 하셔도 안됩니다.
이렇게 사다리 생성(or게임 시작 or 게임방 생성 or 사다리 저장)이라고 정의할 수 있는 독립적인 동작이 완성됐네요.
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.
넵 특히 이 예시를 통해 아직 분리가 덜 됐다는 것이 확연히 보이네요!
Outdated
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.
[Comment]
🎮 컨트롤러 나누기 3
| displayLadder(resultTypes, playerNames, ladder); | |
| @GetMapping("/api/ladders/{id}") | |
| public ResponseEntity<Ladder> createLadder(@PathVariable Long id) { | |
| // 비즈니스 로직, 찾는 작업... (아이디가 존재하지 않는 콘솔 어플리케이션이라해도) | |
| Ladder ladder = 메모린지_디빈지_모를_저장소.findByIdOrNull(id); | |
| // 응답 생성 -> 콘솔로 따지면 outputview가 출력할 수 있게 outputView로 전달 | |
| return ResponseEntity.�body(ladders) | |
| } |
Outdated
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.
[Comment]
🎮 컨트롤러 나누기 4
이 영역도 2-3과 같이 해볼 수 있겠죠?
Outdated
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.
[Comment]
🎮 컨트롤러 나누기 1
이전 리뷰 질문에도 답변을 달았습니다.
Controller는 저희가 게임의 흐름을 관리하도록 만들어진 애는 아니라고 생각해요.
만약 run이라는 것이 1개의 동작이라고 생각한다면 생각을 존중하겠습니다만,
저희는 Controller를 어떤 동작을 할 때 View와 Model의 협업을 위해 연결다리로 둔 대상이죠.
제가 이 밑에 하나의 동작이라고 생각하는 영역들을 묶어볼거에요.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package domain; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class Ladder { | ||
|
|
||
| private final List<Line> lines; | ||
|
|
||
| public Ladder(List<Line> lines) { | ||
| this.lines = lines; | ||
| } | ||
|
|
||
| public List<Line> getLadder() { | ||
| return Collections.unmodifiableList(lines); | ||
| } | ||
| } | ||
|
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. [Comment] getter 만 존재하는 일급컬렉션을 선언하신 이유가 있을까요?
Author
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.
Author
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. 저는 moveAllPlayers 메서드가 '플레이어들이 사다리 위를 움직인다'는 관점으로 봐서 1차 리뷰 수정 때 Players 클래스에 넣었는데 생각해보니까 '사다리' 게임인 만큼 리뷰어 님이 말해주신 저 관점으로 보면 Ladder 클래스에 넣는 게 더 적합할 수도 있겠네요 |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| public class LadderGenerator { | ||
|
|
||
| private boolean randomTrueOrFalse() { | ||
| return Math.random() < 0.5; // 0.0(포함) - 1.0(미포함) 사이의 랜덤한 실수 반환 | ||
SANGHEEJEONG marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| private Point createValue(List<Point> line, int lineIndex) { | ||
| if (lineIndex != 0 && line.get(lineIndex - 1).isEnabled()) { // 이전 값이 true 면 false 반환 | ||
| return Point.DISABLED; | ||
| } | ||
|
|
||
| return Point.FROM_BOOLEAN.apply(randomTrueOrFalse()); | ||
| } | ||
|
|
||
| public Line createLine(int width) { | ||
| List<Point> points = new ArrayList<>(); | ||
| for (int i = 0; i < width - 1; i++) { | ||
| points.add(createValue(points, i)); | ||
| } | ||
|
|
||
| return new Line(points); | ||
| } | ||
|
|
||
| public Ladder createLadder(int width, int height) { | ||
| List<Line> lines = new ArrayList<>(); | ||
| for (int i = 0; i < height; i++) { | ||
| lines.add(createLine(width)); | ||
| } | ||
|
|
||
| return new Ladder(lines); | ||
| } | ||
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package domain; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.List; | ||
|
|
||
| public class Line { | ||
| private final List<Point> points; | ||
|
|
||
| public Line(List<Point> points) { | ||
| this.points = points; | ||
| } | ||
|
|
||
| public boolean canMoveRight(int ladderOrder) { | ||
| return (ladderOrder < points.size()) && (points.get(ladderOrder).isEnabled()); | ||
| } | ||
|
|
||
| public boolean canMoveLeft(int ladderOrder) { | ||
| return (ladderOrder != 0) && (points.get(ladderOrder - 1).isEnabled()); | ||
| } | ||
|
||
|
|
||
| public List<Point> getLine() { | ||
| return Collections.unmodifiableList(points); | ||
| } | ||
|
|
||
| public int getSize() { | ||
| return points.size(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package domain; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class Player { | ||
|
|
||
| private final PlayerName playerName; | ||
| private final Position position; | ||
|
|
||
| public Player(PlayerName name, Position position) { | ||
| this.playerName = name; | ||
| this.position = position; | ||
| } | ||
|
|
||
| public void moveAlongLadder(List<Line> ladder) { | ||
| ladder.forEach(this.position::decideWhereToGo); | ||
| } | ||
|
|
||
| public String getPlayerName() { | ||
| return playerName.getName(); | ||
| } | ||
|
|
||
| public int getPosition() { | ||
| return position.getPosition(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package domain; | ||
|
|
||
| public class PlayerName { | ||
|
|
||
| private final String name; | ||
| private final static int MAX_LENGTH = 5; | ||
| private final static String INVALID_NAME = "all"; | ||
|
Comment on lines
+6
to
+7
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. [Approve] 객체의 규칙을 한눈에 볼 수 있는 상수 정말 좋은데요? 👍 |
||
|
|
||
| public PlayerName(String name) { | ||
| validateName(name); | ||
| this.name = name; | ||
| } | ||
|
|
||
| private void validateName(String name) { | ||
| validateMaxLength(name); | ||
| validateNotBlank(name); | ||
| validateNotEqualAll(name); | ||
| } | ||
|
|
||
| private void validateNotBlank(String name) { | ||
| if (name.isBlank()) { | ||
| throw new IllegalArgumentException("이름은 공백일 수 없습니다."); | ||
| } | ||
| } | ||
|
|
||
| private void validateMaxLength(String name) { | ||
| if (name.length() > MAX_LENGTH) { | ||
| throw new IllegalArgumentException("이름은 " + MAX_LENGTH + "자를 초과할 수 없습니다."); | ||
| } | ||
| } | ||
|
|
||
| private void validateNotEqualAll(String name) { | ||
| if (name.equals(INVALID_NAME)) { | ||
| throw new IllegalArgumentException("이름은 " + INVALID_NAME + "일 수 없습니다."); | ||
| } | ||
| } | ||
|
|
||
| public String getName() { | ||
| return name; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| package domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.NoSuchElementException; | ||
|
|
||
| public class Players { | ||
|
|
||
| private final List<Player> players; | ||
| private final static int MIN_PLAYER_SIZE = 2; | ||
|
|
||
| public Players(List<String> playerNames) { | ||
| validateSize(playerNames); | ||
| this.players = createPlayer(playerNames); | ||
| } | ||
|
|
||
| private void validateSize(List<String> playerNames) { | ||
| if (playerNames.size() < MIN_PLAYER_SIZE) { | ||
| throw new IllegalArgumentException("플레이어 수는 2명 이상이어야 합니다."); | ||
| } | ||
| } | ||
|
|
||
| private List<Player> createPlayer(List<String> playerNames) { | ||
| List<Player> players = new ArrayList<>(); | ||
| for (int i = 0; i < playerNames.size(); i++) { | ||
| players.add(new Player(new PlayerName(playerNames.get(i)), new Position(i))); | ||
| } | ||
|
|
||
| return players; | ||
| } | ||
|
|
||
| public void moveAllPlayers(Ladder ladder) { | ||
| players.forEach(player -> player.moveAlongLadder(ladder.getLadder())); | ||
| } | ||
|
|
||
| public Player findByName(String viewerName) { | ||
| return players.stream() | ||
| .filter(player -> player.getPlayerName().equals(viewerName)) | ||
|
||
| .findFirst() | ||
| .orElseThrow(() -> new NoSuchElementException("플레이어 이름 '" + viewerName + "' 이 존재하지 않습니다.")); | ||
| } | ||
|
|
||
| public List<Player> getPlayers() { | ||
| return Collections.unmodifiableList(players); | ||
| } | ||
|
Comment on lines
+53
to
+55
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. [Approve] 방어적 복사 👍👍 |
||
|
|
||
| public int getPlayersSize() { | ||
| return players.size(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| package domain; | ||
|
|
||
| import java.util.function.Function; | ||
|
|
||
| public enum Point { | ||
| ENABLED(true), | ||
| DISABLED(false); | ||
|
|
||
| private final boolean enabled; | ||
|
|
||
| // 생성자를 통해 상태를 설정 | ||
| Point(boolean enabled) { | ||
| this.enabled = enabled; | ||
| } | ||
|
|
||
| public boolean isEnabled() { | ||
| return enabled; | ||
| } | ||
|
|
||
| public static final Function<Boolean, Point> FROM_BOOLEAN = value -> { | ||
| if (value) { | ||
| return ENABLED; | ||
| } | ||
| return DISABLED; | ||
| }; | ||
| } | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package domain; | ||
|
|
||
| public class Position { | ||
|
|
||
| private int position; | ||
|
|
||
| public Position(int position) { | ||
| this.position = position; | ||
| } | ||
|
|
||
| public void decideWhereToGo(Line line) { | ||
| if (line.canMoveRight(position)) { | ||
| moveRight(line.getSize()); | ||
| return; | ||
| } | ||
|
|
||
| if (line.canMoveLeft(position)) { | ||
| moveLeft(); | ||
| } | ||
| } | ||
|
|
||
| public void moveLeft() { | ||
| if (position != 0) { | ||
| position--; | ||
| } | ||
| } | ||
|
|
||
| public void moveRight(int maxPosition) { | ||
| if (position != maxPosition) { | ||
| position++; | ||
| } | ||
| } | ||
|
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. [Request Change] 이정도 대처로 충분하신가요? 그렇게 프로그램 내부에서 런타임에 예상도 확인도 불가능한 에러가 나게 두면 0인 인덱스가 동일한 층에서 여러개나올 수도 있겠네요... |
||
|
|
||
| public int getPosition() { | ||
| return position; | ||
| } | ||
|
Comment on lines
+25
to
+27
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. [Comment] position을 getter로 꺼내는 방식 보다 조금 더 이 값에 대해서 판단을 이 Position이란 객체에게 맡겨보는 것은 어떨까요? 위의 리뷰들을 참고해서 한번 변경해보면 좋을 것 같아요 |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package domain; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class ResultTypes { | ||
|
|
||
| private final List<String> resultTypes; | ||
|
|
||
| public ResultTypes(List<String> resultTypes, int width) { | ||
| validate(resultTypes, width); | ||
| this.resultTypes = resultTypes; | ||
| } | ||
|
|
||
| private void validate(List<String> resultTypes, int width) { | ||
| resultTypes.forEach(this::validateNotBlank); | ||
| validateSize(resultTypes, width); | ||
| } | ||
|
|
||
| private void validateNotBlank(String resultType) { | ||
| if (resultType.isBlank()) { | ||
| throw new IllegalArgumentException("실행 결과는 공백일 수 없습니다."); | ||
| } | ||
| } | ||
|
|
||
| private void validateSize(List<String> resultTypes, int width) { | ||
| if (resultTypes.size() != width) { | ||
| throw new IllegalArgumentException("실행 결과는 사다리의 개수와 일치해야 합니다."); | ||
| } | ||
| } | ||
|
|
||
| public List<String> getResultTypes() { | ||
| return resultTypes; | ||
| } | ||
| } | ||
|
Comment on lines
+5
to
+34
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. [�Comment] 이 List은 List 같이 의도가 분명한 Wrapper클래스로 감싸지 않은 이유가 있나요?
Author
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. Result에 대한 속성이 단순히 문자형 결과이고 이에 따른 책임이 유효성 검증밖에 없다고 생각해서 굳이 나누지 않았습니다 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. [Comment] 사실 위에서 한 유효성 검증은 ResultType 하나의 요소에 대한 검증이지, List 자체에 대한 검증은 아닌 것 같기도 하네요. 그치만, 저도 상희님이 말씀하신 것 처럼 꼭 래핑할 이유는 없다고 생각해요 해당 클래스만으로도 충분히 의미전달이 된다고 생각합니다 |
||



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.
[Comment]
🎮 컨트롤러 나누기 5
다시 리드미로 돌아와서 생각해보면 좋을 것 같아요.