Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
485f7b7
feat. 사다리 게임을 위한 기본 클래스 생성
dh2906 Aug 12, 2025
ad573e8
feat. 사다리 생성기 구현
dh2906 Aug 12, 2025
a63a639
feat. 사다리 출력 기능 구현
dh2906 Aug 12, 2025
3359aee
feat. 사다리 생성 결과 출력 뷰 생성
dh2906 Aug 12, 2025
904758b
feat. 게임 컨트롤러 생성
dh2906 Aug 12, 2025
8cf5691
feat. 메인 메소드 생성
dh2906 Aug 12, 2025
b12cbc8
feat. 사다리의 너비, 높이에 대한 일급 객체 생성
dh2906 Aug 12, 2025
baf76a8
feat. 입력 뷰 생성
dh2906 Aug 12, 2025
b34c673
feat. 입력값 유효성 검증 클래스 생성
dh2906 Aug 12, 2025
c4f8c24
feat. 커스텀 예외 및 에러 메시지 관리 Enum 생성
dh2906 Aug 12, 2025
d41b964
feat. 일급 객체 적용
dh2906 Aug 12, 2025
826d490
feat. 입력 메소드 적용
dh2906 Aug 12, 2025
c7cd40a
feat. 사다리 결과 구하는 로직 구현
dh2906 Aug 12, 2025
50bdc72
feat. 사다리 타기 결과 출력 추가
dh2906 Aug 12, 2025
9e82cca
refactor. View가 Model에 의존하지 않도록 개선
dh2906 Aug 12, 2025
d22773e
feat. 참가자 이름 입력 메소드 생성
dh2906 Aug 12, 2025
efbeb0e
feat. 참가자 관련 클래스 생성
dh2906 Aug 12, 2025
4453751
feat. 에러 메시지 추가
dh2906 Aug 12, 2025
6112d22
feat. 입력받은 참가자 이름을 리스트로 파싱하는 유틸 메소드 추가
dh2906 Aug 12, 2025
6a31326
chore. 에러 메시지 이름 간결화
dh2906 Aug 12, 2025
a9ddb5d
chore. 유효성 검증 클래스 이름 변경
dh2906 Aug 12, 2025
43ffec6
fix. 수정한 에러 메시지 이름으로 변경
dh2906 Aug 12, 2025
bd68c6f
feat. 게터 추가
dh2906 Aug 12, 2025
4c332df
feat. 사다리 게임 상품 관련 클래스 생성
dh2906 Aug 12, 2025
fa4a311
feat. 상품 입력 추가
dh2906 Aug 12, 2025
47e7017
fix. 사다리 결과 출력 시 List의 Index를 벗어나는 문제 해결
dh2906 Aug 12, 2025
41ad7e0
feat. 에러 메시지 추가 및 변수명 오타 수정
dh2906 Aug 12, 2025
f23302f
fix. int 타입 값 입력 시 개행 문자는 아직 버퍼에 남아있어 이를 비울 수 있도록 수정
dh2906 Aug 12, 2025
d3dd9f3
feat. Player의 이름이 있는 인덱스 구하는 메소드 생성
dh2906 Aug 12, 2025
ddfa603
feat. 전체 플레이어 결과, 단일 플레이어 결과 출력하는 메소드 생성
dh2906 Aug 12, 2025
3ee9373
feat. 게임 흐름 컨트롤러에 적용
dh2906 Aug 12, 2025
0076346
refactor. 컨트롤러 코드 비슷한 역할끼리 메소드로 분할
dh2906 Aug 12, 2025
2bc1732
feat. 플레이어 관련 클래스 테스트 코드 작성
dh2906 Aug 12, 2025
708d795
feat. 사다리 라인 테스트 코드 작성
dh2906 Aug 12, 2025
e23e431
feat. 사다리 테스트 코드 작성
dh2906 Aug 12, 2025
ec6e161
feat. 게터 추가
dh2906 Aug 12, 2025
add62bc
feat. 사다리 생성기 테스트 코드 작성
dh2906 Aug 12, 2025
b2dc9e0
fix. 파싱 메소드에 인자로 null이 들어온 경우 커스텀 예외를 발생하도록 수정
dh2906 Aug 12, 2025
1e47ba4
feat. 에러 메시지 추가
dh2906 Aug 12, 2025
a8bf54f
feat. 파싱 유틸리티 클래스 테스트 코드 작성
dh2906 Aug 12, 2025
cd04c6a
chore. 유효성 검증 클래스 명 변경
dh2906 Aug 12, 2025
414ef4d
feat. 음수가 아닌지 유효성 검증하는 클래스 테스트 코드 작성
dh2906 Aug 12, 2025
af25e2b
refactor. 매직 넘버 상수화
dh2906 Aug 12, 2025
fe893f7
chore. 파일 마지막에 개행 추가
dh2906 Aug 14, 2025
2d755db
chore. 개행 출력을 println에서 \n으로 수정
dh2906 Aug 14, 2025
d24d850
refactor. import문에 와일드 카드 적용
dh2906 Aug 14, 2025
bd9ddb0
refactor. 문자열 상수화
dh2906 Aug 14, 2025
3327486
feat. Line, Ladder 뷰 클래스 생성
dh2906 Aug 14, 2025
112211a
refactor. 모델 내부에서 출력 기능 제거
dh2906 Aug 14, 2025
117f4ac
refactor. LadderView 적용
dh2906 Aug 14, 2025
acedebe
refactor. 컨트롤러 객체 생성 시 의존성 외부에서 주입 받도록 개선
dh2906 Aug 14, 2025
3d32ae6
fix. 메소드 명 수정
dh2906 Aug 14, 2025
16c09ce
style. 중간 개행 문자 제거
dh2906 Aug 14, 2025
b1ed65a
fix. 메소드 명 수정
dh2906 Aug 14, 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
24 changes: 24 additions & 0 deletions src/main/java/LadderGameApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import java.util.Random;
import java.util.Scanner;

import controller.LadderGameController;
import model.LadderGenerator;
import view.InputView;
import view.LadderView;
import view.LineView;
import view.OutputView;

public class LadderGameApplication {

public static void main(String[] args) {
LineView lineView = new LineView();
LadderView ladderView = new LadderView(lineView);
OutputView outputView = new OutputView(ladderView);
InputView inputView = new InputView(new Scanner(System.in));
LadderGenerator ladderGenerator = new LadderGenerator(new Random());

LadderGameController ladderGameController = new LadderGameController(ladderGenerator, outputView, inputView);

ladderGameController.run();
}
}
68 changes: 68 additions & 0 deletions src/main/java/controller/LadderGameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package controller;

import java.util.List;
import java.util.Random;
import java.util.Scanner;

import model.*;
import util.Parser;
import view.InputView;
import view.LadderView;
import view.LineView;
import view.OutputView;

public class LadderGameController {

private final LadderGenerator ladderGenerator;
private final OutputView outputView;
private final InputView inputView;

private static final String VIEW_ALL = "all";

public LadderGameController(
LadderGenerator ladderGenerator,
OutputView outputView,
InputView inputView
) {
this.ladderGenerator = new LadderGenerator(new Random());
this.outputView = new OutputView(new LadderView(new LineView()));
this.inputView = new InputView(new Scanner(System.in));
}

public void run() {

Choose a reason for hiding this comment

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

run() 메서드가 확실히 깔끔해진것 같네요
메서드 분리 잘하신것 같아요 👍

Players players = inputPlayers();
Prizes prizes = inputPrizes();

Width width = new Width(players.size());
Height height = new Height(inputView.inputHeight());
Ladder ladder = ladderGenerator.generate(height, width);

outputView.printExecuteResult(ladder, players, prizes);

printResultByViewerName(ladder, players, prizes);
}

private Players inputPlayers() {
String strPlayerNames = inputView.inputPlayerNames();
List<String> playerNames = Parser.parseStringToList(strPlayerNames);

return new Players(playerNames);
}

private Prizes inputPrizes() {
String strPrizeNames = inputView.inputPrizeNames();
List<String> prizeNames = Parser.parseStringToList(strPrizeNames);

return new Prizes(prizeNames);
}

private void printResultByViewerName(Ladder ladder, Players players, Prizes prizes) {
String resultViewerName = inputView.inputResultViewerName();

if (resultViewerName.equalsIgnoreCase(VIEW_ALL)) {
outputView.printAllPlayerResult(ladder, players, prizes);
} else {
outputView.printSinglePlayerResult(ladder, players, prizes, resultViewerName);
}
}
}
8 changes: 8 additions & 0 deletions src/main/java/exception/CustomException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package exception;

public class CustomException extends IllegalArgumentException {

public CustomException(ErrorMessage errorMessage) {
super(errorMessage.getMessage());
}
}
24 changes: 24 additions & 0 deletions src/main/java/exception/ErrorMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package exception;

public enum ErrorMessage {

NOT_INTEGER_FORMAT("int 타입 정수가 아닌 값이 입력되었습니다."),
NEGATIVE_NUMBER("음수가 입력되었습니다."),
EMPTY_NAME("이름이 비어있습니다."),
NAME_LENGTH("이름이 5글자를 넘었습니다."),
PLAYER_NAMES_EMPTY("참여자가 존재하지 않습니다"),
PRIZE_EMPTY("당첨 결과가 비어있습니다."),
PRIZES_EMPTY("당첨 결과 리스트가 비어있습니다."),
PLAYER_NOT_FOUND("해당 플레이어를 찾을 수 없습니다."),
NULL_INPUT_STRING("입력 문자열로 null이 입력 되었습니다.");
Comment on lines +5 to +13

Choose a reason for hiding this comment

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

👍


private final String message;

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

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

import util.SizeValidator;

public class Height {

private final int height;

public Height(int height) {
SizeValidator.validateNotNegative(height);

Choose a reason for hiding this comment

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

이렇게 분리했을때의 장점은 뭐가 있을까요?

Copy link
Author

@dh2906 dh2906 Aug 14, 2025

Choose a reason for hiding this comment

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

HeightWidth에서 음수 값인지 검증하는 로직이 중복되는 일이 발생하여 분리했습니다.
제 생각으로는 중복 로직의 최소화할 수 있으며, 유효성 검증의 요구 사항이 변경될 경우 해당 클래스에서만 코드를 수정하면 된다는 장점이 존재합니다!

this.height = height;
}

public int getHeight() {
return height;
}
}
28 changes: 28 additions & 0 deletions src/main/java/model/Ladder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package model;

import java.util.List;

public class Ladder {

private final Height height;
private final List<Line> lines;

Choose a reason for hiding this comment

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

List을 총괄하는 Lines 일급 컬렉션을 만들어도 좋을 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

좋은 의견 감사합니다!


public Ladder(Height height, List<Line> lines) {
this.height = height;
this.lines = lines;
}

public int move(int index) {
int position = index;

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

return position;
}

public List<Line> getLines() {
return lines;
}
}
38 changes: 38 additions & 0 deletions src/main/java/model/LadderGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package model;

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

public class LadderGenerator {

private final Random random;

public LadderGenerator(Random random) {
this.random = random;
}

public Ladder generate(Height height, Width width) {
List<Line> lines = new ArrayList<>();

for (int i = 0; i < height.getHeight(); i++) {
lines.add(new Line(generateConnections(width)));
}

return new Ladder(height, lines);
}

private List<Boolean> generateConnections(Width width) {
List<Boolean> connections = new ArrayList<>();

for (int i = 0; i < width.getWidth() - 1; i++) {
if (i > 0 && connections.get(i - 1)) {
connections.add(false);
} else {
connections.add(random.nextBoolean());
}
}

return connections;
}
}
26 changes: 26 additions & 0 deletions src/main/java/model/Line.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package model;

import java.util.List;

public class Line {

Choose a reason for hiding this comment

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

Model 에서 출력의 역할도 하고 있는것 같은데 이러면 변경이 생겼을때 수정이 어려워질것 같은데 어떻게 생각하시나요??

Copy link
Author

Choose a reason for hiding this comment

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

저도 이 부분에 대해 고민을 했습니다. Model에서 바로 출력하는 것이 아닌 출력을 하기 위한 문자열을 반환하도록 하는 것은 어떨까 하는 생각도 해봤지만 역시 LineView 라는 클래스로 분리하는 것이 더 맞는 것 같네요!!


private final List<Boolean> connections;

public Line(List<Boolean> connections) {
this.connections = connections;
}

public int move(int index) {
if (index < connections.size() && connections.get(index)) {
return index + 1;
} else if (index > 0 && connections.get(index - 1)) {
return index - 1;
}

return index;
}

public List<Boolean> getConnections() {
return connections;
}
Comment on lines +23 to +25

Choose a reason for hiding this comment

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

이 메서드는 어떤 역할을 하나요?
생성자를 통해서 List<Boolean> 을 받아오는데 그대로 반환하는 경우가 궁금해용

Copy link
Author

Choose a reason for hiding this comment

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

해당 부분은 기능 자체에서는 사용하지 않지만 테스트 코드를 작성하면서 필요하기에 추가했습니다.
그대로 반환하게 된다면 값이 외부에서 수정될 우려가 존재할 수 있겠네요.... 그렇다면 새로운 리스트에 connections에 있는 요소를 복사해서 반환하도록 해야 할 것 같습니다.

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

import exception.CustomException;
import exception.ErrorMessage;

public class Player {

private final String name;

private static final int MAX_NAME_LENGTH = 5;

public Player(String name) {
validate(name);
this.name = name;
}

public String getName() {
return name;
}

private void validate(String name) {
validateNameEmpty(name);
validateNameLength(name);
}

private void validateNameEmpty(String name) {
if (name == null || name.isBlank()) {
throw new CustomException(ErrorMessage.EMPTY_NAME);
}
}

private void validateNameLength(String name) {
if (name.length() > MAX_NAME_LENGTH) {
throw new CustomException(ErrorMessage.NAME_LENGTH);
}
}
}
50 changes: 50 additions & 0 deletions src/main/java/model/Players.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package model;

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

import exception.CustomException;
import exception.ErrorMessage;

public class Players {

private final List<Player> players;

public Players(List<String> playerNames) {
validateEmpty(playerNames);
this.players = init(playerNames);
}

public void validateEmpty(List<String> playerNames) {
if (playerNames == null || playerNames.isEmpty()) {
throw new CustomException(ErrorMessage.PLAYER_NAMES_EMPTY);
}
}

private List<Player> init(List<String> playerNames) {
List<Player> result = new ArrayList<>();

for (String name : playerNames) {
result.add(new Player(name));
}

return result;
}

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

public List<Player> getPlayers() {
return players;
}

public int indexOf(String name) {
for (int i = 0; i < players.size(); i++) {
if (players.get(i).getName().equals(name)) {
return i;
}
}
throw new CustomException(ErrorMessage.PLAYER_NOT_FOUND);
}
}
24 changes: 24 additions & 0 deletions src/main/java/model/Prize.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package model;

import exception.CustomException;
import exception.ErrorMessage;

public class Prize {

private final String prize;

public Prize(String prize) {
validateEmpty(prize);
this.prize = prize;
}

private void validateEmpty(String prize) {
if (prize == null || prize.isBlank()) {
throw new CustomException(ErrorMessage.PRIZE_EMPTY);
}
}

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

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

import exception.CustomException;
import exception.ErrorMessage;

public class Prizes {

private final List<Prize> prizes;

public Prizes(List<String> prizes) {
validateEmpty(prizes);
this.prizes = init(prizes);
}

private void validateEmpty(List<String> prizes) {
if (prizes == null || prizes.isEmpty()) {
throw new CustomException(ErrorMessage.PRIZES_EMPTY);
}
}

private List<Prize> init(List<String> prizes) {
List<Prize> result = new ArrayList<>();

for (String name : prizes) {
result.add(new Prize(name));
}

return result;
}

public List<Prize> getPrizes() {
return prizes;
}
}
Loading