Skip to content

[LBP] 권지후 사다리 2-5단계 미션 제출합니다 #44

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

Open
wants to merge 48 commits into
base: jihoo2002
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
8ff892d
feat 사다리 1단계
jihoo2002 Mar 7, 2025
272e3f3
fix else문 수정
jihoo2002 Mar 7, 2025
79a89dd
refactor 코드리뷰 바탕 리펙토링 진행
jihoo2002 Mar 9, 2025
58449af
refactor 네이밍 리펙토링
jihoo2002 Mar 9, 2025
a15ea17
사다리 2-5단계 완성
jihoo2002 Mar 17, 2025
61dac53
refactor pointGeneratorTest에서 중복된 코드리펙토링
jihoo2002 Mar 17, 2025
5894df8
refactor HeightTest displayname 추가 및 네이밍 컨벤션 일치
jihoo2002 Mar 17, 2025
72d6d7d
Merge branch 'jihoo2002' into step2
jihoo2002 Mar 17, 2025
cf29f3e
refactor 필요없는 개행 리펙토링
jihoo2002 Mar 17, 2025
8409652
Merge branch 'step2' of https://github.com/jihoo2002/java-ladder-func…
jihoo2002 Mar 17, 2025
26d4ae4
refactor 플레이어 수에 따라 리스트를 나눠 라인 생성
jihoo2002 Mar 19, 2025
03c0883
refactor 필요없는 상수 제거 및 상수화
jihoo2002 Mar 19, 2025
586cbf0
refactor players 네이밍 리펙토링
jihoo2002 Mar 19, 2025
4cc30cc
refactor MIN, MAX로 네이밍 컨벤션 일치화
jihoo2002 Mar 19, 2025
c7151e9
refactor MIN 오타 수정
jihoo2002 Mar 19, 2025
e7385c6
refactor static 메서드명 리펙토링
jihoo2002 Mar 19, 2025
7a08d40
refactor Prize 클래스 마지막줄 개행
jihoo2002 Mar 19, 2025
ed8b69e
feat 예외메세지를 관리하는 ExceptionMessage 열거형 클래스 추가
jihoo2002 Mar 19, 2025
62e1bce
refactor 예외 메세지에 ExceptionMessage 열거형 클래스 사용
jihoo2002 Mar 19, 2025
8b1cb04
refactor 수정된 static 메서드 명 적용
jihoo2002 Mar 19, 2025
6f4ef52
refactor calculateResults 메서드 분리 및 인덴트 개선
jihoo2002 Mar 19, 2025
a2a5707
refactor HeightTest displayname 리펙토링
jihoo2002 Mar 19, 2025
c3e9176
refactor LadderGameTest displayname 리펙토링
jihoo2002 Mar 19, 2025
522379a
refactor LadderResultTest에수정된 static 메서드 명 적용
jihoo2002 Mar 19, 2025
8aafd4f
refactor LadderResultTest displayname 리펙토링
jihoo2002 Mar 19, 2025
a7f648c
refactor LadderTest displayname 리펙토링
jihoo2002 Mar 19, 2025
42140af
refactor LineTest displayname 리펙토링
jihoo2002 Mar 19, 2025
93c7578
refactor PlayersTest displayname 리펙토링
jihoo2002 Mar 19, 2025
871892e
refactor PlayerTest displayname 리펙토링
jihoo2002 Mar 19, 2025
c2facf9
refactor PointGeneratorTest displayname 리펙토링
jihoo2002 Mar 19, 2025
5ff0bac
refactor PrizesTest에서 수정된 static 메서드 명 적용
jihoo2002 Mar 19, 2025
5ae6601
refactor PrizesTest displayname 리펙토링
jihoo2002 Mar 19, 2025
2b0164d
refactor PrizeTest에서 파라미터화된 테스트로 예외처리 검증, displayname 리펙토링
jihoo2002 Mar 19, 2025
1335675
refactor Prize에서 공백문자열에 대한 유효성 검사 개선
jihoo2002 Mar 19, 2025
903cbf5
refactor PrizeTest에서 빈 문자열 및 공백에 대한 예외 처리 파라미터화 테스트으로 리펙토링
jihoo2002 Mar 19, 2025
71f176b
refactor RandomValueGeneratorTest displayname 리펙토링
jihoo2002 Mar 19, 2025
3a1f208
refactor SizeTest displayname 리펙토링
jihoo2002 Mar 19, 2025
1e16ec9
refactor FixedNumberGenerator 클래스를 테스트 디렉토리로 이동
jihoo2002 Mar 19, 2025
0c4b52f
refactor 이동 로직을 Ladder와 Line으로 책임 분리
jihoo2002 Mar 19, 2025
c938035
refactor Line 클래스에서 라인 생성 책임 위임
jihoo2002 Mar 19, 2025
2020d05
refactor LadderGame 제거 및 startLadder 메서드 내에서 로직 통합
jihoo2002 Mar 19, 2025
c678058
refactor startLadder 메서드 간결화 및 기능 분리
jihoo2002 Mar 19, 2025
34ca870
refactor Player 생성 시 유효성 검사 로직 추가
jihoo2002 Mar 20, 2025
38a1359
refactor PlayerTest 유효성 검사 테스트 추가 및 리펙토링
jihoo2002 Mar 20, 2025
31cca69
refactor FixedNumberGenerator 클래스를 테스트 util 패키지로 이동
jihoo2002 Mar 20, 2025
70f1f56
refactor 에러 메세지 네이밍 컨벤션 통일 및 가독성 개선
jihoo2002 Mar 20, 2025
8f215c1
refactor PlayerTest hasMessage 리펙토링
jihoo2002 Mar 20, 2025
9e6496d
refactor import 문 와일드카드 리펙토링 및 개행 수정
jihoo2002 Mar 20, 2025
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
69 changes: 53 additions & 16 deletions src/main/java/controller/LadderController.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,62 @@
package controller;

import model.*;
import model.Height;
import model.Players;
import model.PointGenerator;
import model.Prizes;
import model.Ladder;
import model.LadderResult;
import model.RandomValueGenerator;
import model.Point;
import view.InputView;
import view.ResultView;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static model.Point.HAS_POINT;

public class LadderController {

private static final int LADDER_SIZE = 4;
private static final int CHUNK_SIZE = 3;
private static final String ALL_PLAYERS = "all";

Choose a reason for hiding this comment

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

"all" 이라는 문자열 리터럴이 모든 플레이어들을 나타낸다고 생각하니 어색한 것 같아요. 어떻게 생각하시나요?

Copy link
Author

Choose a reason for hiding this comment

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

image
미션 LMS에 실행결과에서의 예시가 all로 명시되어 있어
all 이라는 문자값을 넣어주되 상수 명을 ALL_PLAYERS라고 명시해줬습니다 !

private final ResultView resultView = new ResultView();
private final InputView inputView = new InputView();

public void startLadder() {
PointGenerator pointGenerator = new PointGenerator(new Random());
LadderGame ladderGame = new LadderGame(new Size(LADDER_SIZE), new Size(LADDER_SIZE), pointGenerator);
List<Point> ladderPoints = ladderGame.getLadderPoints();
List<Boolean> points = formatLadderPoints(ladderPoints);
List<List<Boolean>> ladderLines = processLadderLines(points);
resultView.printLadder(ladderLines);
Players players = new Players(inputView.inputNames());
Prizes prizes = Prizes.createPrizes(inputView.inputResult(), players);
Height height = new Height(inputView.getMaxLadderHeight());
PointGenerator pointGenerator = new PointGenerator(new RandomValueGenerator());

Ladder ladder = Ladder.createLadder(players.size(), height.getValue(), pointGenerator);
LadderResult ladderResult = calculateLadderResult(ladder, players.getPlayers(), prizes);

printLadder(ladder, players, prizes);
printResult(ladderResult);
}

private LadderResult calculateLadderResult(Ladder ladder, List<String> players, Prizes prizes) {
LadderResult ladderResult = new LadderResult(ladder);
ladderResult.calculateResults(players, prizes);
return ladderResult;
}

private void printLadder(Ladder ladder, Players players, Prizes prizes) {
List<Boolean> points = formatLadderPoints(ladder.getPointsFromLines());
List<List<Boolean>> ladderLines = processLadderLines(points, players.size());
resultView.printLadder(ladderLines, players.getPlayers(), prizes.getPrize());
}

private void printResult(LadderResult ladderResult) {
while (true) {
String targetPlayerName = inputView.getTargetPlayerName();

if (targetPlayerName.equals(ALL_PLAYERS)) {
resultView.printAllResults(ladderResult.getValue());
break;
}
if (ladderResult.getValue().containsKey(targetPlayerName)) {
resultView.printSingleResult(ladderResult.getResultForPlayer(targetPlayerName));
}
}
}

private List<Boolean> formatLadderPoints(List<Point> ladderPoints) {
Expand All @@ -31,15 +67,16 @@ private List<Boolean> formatLadderPoints(List<Point> ladderPoints) {
return formattedLadder;
}

private List<List<Boolean>> processLadderLines(List<Boolean> ladderPoints) {
private List<List<Boolean>> processLadderLines(List<Boolean> ladderPoints, int numPlayers) {
int lineCount = numPlayers - 1;
List<List<Boolean>> chunks = new ArrayList<>();
for (int i = 0; i < ladderPoints.size(); i += CHUNK_SIZE) {
chunks.add(getLadderSegment(ladderPoints, i));
for (int i = 0; i < ladderPoints.size(); i += lineCount) {
chunks.add(getLadderSegment(ladderPoints, i, lineCount));
}
return chunks;
}

private List<Boolean> getLadderSegment(List<Boolean> ladderPoints, int startIndex) {
return ladderPoints.subList(startIndex, Math.min(startIndex + CHUNK_SIZE, ladderPoints.size()));
private List<Boolean> getLadderSegment(List<Boolean> ladderPoints, int startIndex, int lineCount) {
return ladderPoints.subList(startIndex, Math.min(startIndex + lineCount, ladderPoints.size()));
}
}
24 changes: 24 additions & 0 deletions src/main/java/exception/ExceptionMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package exception;

public enum ExceptionMessage {
LADDER_HEIGHT_MIN_VALUE("사다리 높이는 최소 2 이상이어야 합니다."),
LADDER_SIZE_NEGATIVE("사다리 사이즈가 0보다 작을 수는 없습니다."),
LADDER_HEIGHT_NOT_NUMBER("사다리 높이는 숫자여야 합니다."),

PLAYER_NAME_MAX_LENGTH_EXCEEDED("참가자 이름은 최대 5글자를 초과할 수 없습니다."),
MIN_PLAYERS_REQUIRED("참가자는 최소 2명 이상이여야 합니다."),

RESULT_COUNT_MISMATCH("실행결과 개수와 참가자의 수는 동일해야 합니다."),
RESULT_NOT_NULL_OR_EMPTY("실행결과는 null이거나 공백일 수는 없습니다."),
NULL_OR_EMPTY_INPUT("입력값이 null이거나 비어있을 순 없습니다.");
Comment on lines +3 to +13

Choose a reason for hiding this comment

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

좋은 시도인 것 같아요. 다만 바뀔 수 있는 규칙에 대해서는 고민해보면 좋을 것 같아요! 👍
예: 사다리 최소 높이가 3으로 변경되었을 때, 이 메시지에서도 변경해줘야 한다.

Copy link
Author

Choose a reason for hiding this comment

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

이 부분은 String.format() 와 같은 포멧팅을 사용하여 메세지를 동적으로 관리할 수 있을 것 같습니다
앞으로 규칙값이 변경될 가능성을 염두에 두고 포멧팅 방법 등을 참고해보겠습니다.
좋은 피드백 감사합니다 !


private final String message;

ExceptionMessage(String message) {
this.message = message;
}

public String getMessage() {
return message;
}
}
24 changes: 24 additions & 0 deletions src/main/java/model/Height.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package model;

import exception.ExceptionMessage;

public class Height {
private static final int MIN_HEIGHT = 2;

private final int value;

public Height(int value) {
validateValue(value);
this.value = value;
}

private void validateValue(int value) {
if (value < MIN_HEIGHT) {
throw new IllegalArgumentException(ExceptionMessage.LADDER_HEIGHT_MIN_VALUE.getMessage());
}
}

public int getValue() {
return value;
}
}
16 changes: 16 additions & 0 deletions src/main/java/model/Ladder.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,27 @@ public Ladder(List<Line> lines) {
this.lines = List.copyOf(lines);
}

public static Ladder createLadder(int playerCount, int maxHeight, PointGenerator pointGenerator) {
List<Line> lines = Line.createLines(playerCount, maxHeight, pointGenerator);
return new Ladder(lines);
}

public int move(int position) {
for (Line line : lines) {
position = line.move(position);
}
return position;
}

public List<Point> getPointsFromLines() {
List<Point> result = new ArrayList<>();
for (Line line : lines) {
result.addAll(line.getPointGroups());
}
return List.copyOf(result);
}

public List<Line> getLines() {
return List.copyOf(lines);
}
}
27 changes: 0 additions & 27 deletions src/main/java/model/LadderGame.java

This file was deleted.

35 changes: 35 additions & 0 deletions src/main/java/model/LadderResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package model;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class LadderResult {
private static final String NO_RESULT = "결과 없음";
private final Map<String, String> results;
private final Ladder ladder;
Comment on lines +8 to +11

Choose a reason for hiding this comment

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

도메인, 출력
어디에 더 가까운가요?
무엇을 책임지고 있나요?

Copy link
Author

@jihoo2002 jihoo2002 Mar 29, 2025

Choose a reason for hiding this comment

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

도메인 클래스라고 생각합니다
결과를 포맷팅해서 출력하는 역할이 없고,
결과를 계산하는 비즈니스 로직을 책임지고 있기 때문에
LadderResult는 도메인 클래스라고 생각합니다.


public LadderResult(Ladder ladder) {
this.ladder = ladder;
this.results = new HashMap<>();
}

public void calculateResults(List<String> playerNames, Prizes prizes) {
List<String> prizeValues = prizes.getPrize();

for (String playerName : playerNames) {
int playerIndex = playerNames.indexOf(playerName);
playerIndex = ladder.move(playerIndex);
results.put(playerName, prizeValues.get(playerIndex));
}
}

public String getResultForPlayer(String name) {
return results.getOrDefault(name, NO_RESULT);
}

public Map<String, String> getValue() {
return Collections.unmodifiableMap(results);
}
}
27 changes: 27 additions & 0 deletions src/main/java/model/Line.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package model;

import java.util.List;
import java.util.stream.IntStream;

public class Line {
private final List<Point> points;
Expand All @@ -9,6 +10,32 @@ public Line(List<Point> points) {
this.points = List.copyOf(points);
}

public static List<Line> createLines(int playerCount, int maxHeight, PointGenerator pointGenerator) {
return IntStream.range(0, maxHeight)
.mapToObj(i -> new Line(pointGenerator.createLinePoints(new Size(playerCount))))
.toList();
}

public int move(int position) {
if(canMoveLeft(position)) {
return position -1;
}

if(canMoveRight(position)) {
return position + 1;
}
return position;
}

private boolean canMoveLeft(int position) {
return position > 0 && points.get(position - 1) == Point.HAS_POINT;
}

private boolean canMoveRight(int position) {
return position < points.size() && points.get(position) == Point.HAS_POINT;

}

public List<Point> getPointGroups() {
return List.copyOf(points);
}
Expand Down
28 changes: 28 additions & 0 deletions src/main/java/model/Player.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package model;

import exception.ExceptionMessage;

public class Player {

private static final int MAX_NAME_LENGTH = 5;

Choose a reason for hiding this comment

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

MINIMUM의 줄임말로 MIN을 사용하고, MAXIMUM의 줄임말로 MAX를 사용하는 것 같아요.

그러나 현재 프로젝트에서는 MAX도 있고 MINIMUM도 쓰이고 있어요.
표현 방식을 통일하는 것은 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

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

그때그때 떠오르는 대로 변수명을 선언했는데, 표현 방식을 더 통일해보겠습니다
ExceptionMessage 클래스 내에서 표현통일해서 다시 네이밍 리펙토링 하였습니다 감사합니다

private final String value;

Choose a reason for hiding this comment

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

Player의 value는 무엇일까요?

Copy link
Author

Choose a reason for hiding this comment

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

이 피드백 받고 저도 value가 뭐였지,, 하면서 다시 코드를 살펴보는 것 자체가
네이밍이 명확치 않아서 생긴 문제라고 생각되네요ㅜㅜ

플레이어 이름을 나타내는value 변수명이 명확하지 않아, playerName으로
다시 네이밍하는 것이 더 직관적이고 명확하다고 생각됩니다. 감사합니다


public Player(String value) {
validateValues(value);
this.value = value;
}

public String getValue() {
return value;
}

private void validateValues(String value) {
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException(ExceptionMessage.NULL_OR_EMPTY_INPUT.getMessage());
}

if (value.length() > MAX_NAME_LENGTH) {
throw new IllegalArgumentException(ExceptionMessage.PLAYER_NAME_MAX_LENGTH_EXCEEDED.getMessage());
}
}
}
38 changes: 38 additions & 0 deletions src/main/java/model/Players.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package model;

import exception.ExceptionMessage;

import java.util.List;

public class Players {

private static final int MIN_PLAYER_LENGTH = 2;
private final List<Player> players;

public Players(List<String> players) {
validatePlayers(players);
this.players = generatePlayers(players);
}

public int size() {
return players.size();
}

public List<String> getPlayers() {
return players.stream()
.map(Player::getValue)
.toList();
}

private List<Player> generatePlayers(List<String> players) {
return players.stream()
.map(Player::new)
.toList();
}
Comment on lines +27 to +31

Choose a reason for hiding this comment

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

Suggested change
private List<Player> generatePlayers(List<String> players) {
return players.stream()
.map(Player::new)
.toList();
}
private List<Player> generatePlayers(List<String> playerNames) {
return playerNames.stream()
.map(Player::new)
.toList();
}

더 명확히 써볼 수 있을 것 같아요. 😄

Copy link
Author

@jihoo2002 jihoo2002 Mar 29, 2025

Choose a reason for hiding this comment

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

말씀하신 것처럼 코드 작성 시에는 제가 제 코드를 가장 잘 알기 때문에
"이 정도면 되겠지" 하고 넘아가곤 했는데,
협업이나 현업에서는 다른 사람들이 코드를 읽을 가능성도 있으므로
더 많은 사람들과 코드를 공유할 때를 고려해서 가독성 좋게 코드를 작성하려고 노력하겠습니다
피드백 주셔서 감사드립니다 !


private void validatePlayers(List<String> players) {
if (players.size() < MIN_PLAYER_LENGTH) {
throw new IllegalArgumentException(ExceptionMessage.MIN_PLAYERS_REQUIRED.getMessage());
}
}
}
9 changes: 4 additions & 5 deletions src/main/java/model/PointGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class PointGenerator {
private final Random random;
private final RandomUtil randomUtil;

public PointGenerator(Random random) {
this.random = random;
public PointGenerator(RandomUtil randomUtil) {
this.randomUtil = randomUtil;
}

public List<Point> createLinePoints(Size width) {
Expand All @@ -28,7 +27,7 @@ private Point createPoint(boolean isPreviousLine) {
if (isPreviousLine) {
return Point.NO_POINT;
}
int randomIndex = random.nextInt(points.size());
int randomIndex = randomUtil.generateRandomNumber();
return points.get(randomIndex);
}
}
23 changes: 23 additions & 0 deletions src/main/java/model/Prize.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package model;

import exception.ExceptionMessage;

public class Prize {

private final String value;

public Prize(String value) {
validateValue(value);
this.value = value;
}

public String getValue() {
return value;
}

private void validateValue(String value) {
if (value == null || value.trim().isEmpty()) {
throw new IllegalArgumentException(ExceptionMessage.RESULT_NOT_NULL_OR_EMPTY.getMessage());
}
}
}
Loading