Skip to content
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

[사다리 미션] 신혜빈 미션 제출합니다. #20

Open
wants to merge 25 commits into
base: shin378378
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3d3d769
docs : README파일 작성
c0mpuTurtle Oct 29, 2024
b85fa13
feat : 후보자 행 객체 생성
c0mpuTurtle Oct 29, 2024
e2342f2
feat : 행 객체 생성
c0mpuTurtle Oct 29, 2024
5c01a53
refactor : 사다리 객체 다시 구성
c0mpuTurtle Oct 29, 2024
ceb91b9
feat : Controller 생성
c0mpuTurtle Oct 29, 2024
b753e93
feat : inputView 생성
c0mpuTurtle Oct 29, 2024
651f22b
2단계 사다리 생성까지 완성
c0mpuTurtle Oct 29, 2024
9f1d637
feat : 참가자, 실행결과 객체 생성 기능 추가
c0mpuTurtle Oct 29, 2024
42c7d00
feat : 사다리 결과 출력 기능 추가
c0mpuTurtle Oct 30, 2024
14b4d35
4단계까지 완성
c0mpuTurtle Oct 30, 2024
210d7f3
test : 이름 글자수 오류 발생하는지 확인
c0mpuTurtle Oct 30, 2024
9739d30
docs : README 작성
c0mpuTurtle Oct 30, 2024
6422685
refactor : all을 입력받으면 멈추는 기능 do-while문으로 구현하기
c0mpuTurtle Nov 8, 2024
cd7c8a9
refactor : 특정 참가자의 결과 찾는 기능을 OutputView가 아닌 Participants로 옮기기
c0mpuTurtle Nov 8, 2024
f2642b3
refactor : 플레이어들과 결과들 생성하고 나서 리턴값 바꾸기
c0mpuTurtle Nov 8, 2024
62ded33
refactor : ladderRows를 Ladder객체로 입력받기
c0mpuTurtle Nov 8, 2024
b24b654
feat : enum 사용하기
c0mpuTurtle Nov 8, 2024
8b4800c
refactor : 불필요한 파라미터 없애기
c0mpuTurtle Nov 8, 2024
af2eec1
fix : README 수정하기
c0mpuTurtle Nov 13, 2024
e3d3190
feat : 00rl
c0mpuTurtle Nov 14, 2024
fa8e9e0
refactor : static 변수와 일반 변수는 한 줄 띄우기
c0mpuTurtle Nov 15, 2024
b21fef9
refactor : Players의 생성 방법 바꾸기
c0mpuTurtle Nov 15, 2024
d0a5f26
feat : PlayerResultDto 생성하기
c0mpuTurtle Nov 16, 2024
8a15278
refactor : Math.random()을 ThreadLocalRandom으로 수정하기
c0mpuTurtle Nov 16, 2024
37a8e9d
refactor : randomConnectedOrDisconnected로 메소드명 수정하기
c0mpuTurtle Nov 16, 2024
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
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# java-ladder

Choose a reason for hiding this comment

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

헉 리드미를 작성해주셔서 한 눈에 보기가 편하네요!


# 기능요구사항
## 참여자
- [x] 참여할 사람의 이름을 입력받는다.
- [x] 참여하는 사람을 따로 관리한다.
## 사다리 결과
- [x] 실행결과를 입력받는다.
- [x] 실행결과를 따로 관리한다.
## 사다리
- [x] 최대 사다리 높이를 입력받는다.
- [x] 최대 사다리 높이 만큼 사다리를 생성한다.
- [x] 사다리를 출력한다.
## 실행 결과
- [x] 사람마다 실행 결과를 따로 저장한다.
- [x] 특정 사람의 실행 결과를 출력한다.
## 종료
- [x] "all"이 입력될 경우 실행을 종료한다.

# 예외처리
- [x] 참가자 이름이 5자 넘어가는 경우 오류 생성
- [x] 특정 사용자 결과를 찾는 데 특정 사용자가 없는 경우 예외 발생
8 changes: 8 additions & 0 deletions src/main/java/LadderApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import controller.LadderController;

public class LadderApplication {
public static void main(String[] args) {
LadderController controller = new LadderController();
controller.playLadderGame();
}
}
76 changes: 76 additions & 0 deletions src/main/java/controller/LadderController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package controller;

import model.ladder.Ladder;
import model.ladder.LadderRow;
import model.player.Player;
import model.player.PlayerResultDto;
import model.player.PlayerResults;
import model.player.Players;
import model.tool.Splitter;
import view.InputView;
import view.OutputView;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;


public class LadderController {
static final InputView inputView = new InputView();
static final Splitter splitter = new Splitter();
static final OutputView outputView = new OutputView();

private Players createPlayers() {
String playerNamesBeforeSplit = inputView.inputPlayers();
String[] playerNames = splitter.splitWithComma(playerNamesBeforeSplit);
Players players = new Players(playerNames);
return players;
}

private List<String> createLadderResults() {
String trialResultsBeforeSplit = inputView.inputResults();
String[] trialResults = splitter.splitWithComma(trialResultsBeforeSplit);
List<String> ladderResults = Arrays.asList(trialResults);
return ladderResults;
}

private Ladder createLadder(int columnSize) {
List<String> ladderResults = createLadderResults();
int rowSize = inputView.inputLadderHeight();
List<LadderRow> ladderRows = new ArrayList<>();
for (int i = 0; i < rowSize; i++) {
LadderRow ladderRow = new LadderRow(columnSize);
ladderRows.add(ladderRow);
}
Ladder ladder = new Ladder(ladderRows, ladderResults);
return ladder;
}

private PlayerResults createPlayerResults(Players players, Ladder ladder) {
List<Player> playerInventory = players.getPlayerInventory();
List<PlayerResultDto> playerResultsInventory = ladder.decidePlayerResults(playerInventory);
PlayerResults playerResults = new PlayerResults(playerResultsInventory);
return playerResults;
}

private void printLadder(Players players, Ladder ladder) {
List<LadderRow> ladderRows = ladder.getLadderRows();
List<String> ladderResults = ladder.getLadderResults();
List<Player> playerInventory = players.getPlayerInventory();
OutputView outputView = new OutputView();
outputView.outputLadder(playerInventory, ladderRows, ladderResults);
}

public void playLadderGame() {
Players players = createPlayers();
Ladder ladder = createLadder(players.getPlayerInventory().size() - 1);
PlayerResults playerResults = createPlayerResults(players, ladder);
printLadder(players, ladder);
String playerName;
do {
playerName = inputView.inputPlayerToWantResult();
outputView.outputPlayerResult(playerResults, playerName);
} while (!playerName.equals("all"));
}
}
62 changes: 62 additions & 0 deletions src/main/java/model/ladder/Ladder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package model.ladder;

import model.player.Player;
import model.player.PlayerResultDto;

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

public class Ladder {
private List<LadderRow> ladderRows;
private List<String> ladderResults;

public Ladder(List<LadderRow> ladderRows, List<String> ladderResults) {
this.ladderRows = ladderRows;
this.ladderResults = ladderResults;
}

private LadderMove changePosition(List<Boolean> points, int columnPosition) {
if (columnPosition > 0 && points.get(columnPosition - 1)) {
return LadderMove.LEFT;
}
if (columnPosition < points.size() && points.get(columnPosition)) {
return LadderMove.RIGHT;
}
return LadderMove.DOWN;
}

private int moveColumn(List<Boolean> points, int columnPosition) {
LadderMove moveDirection = changePosition(points, columnPosition);
return columnPosition + moveDirection.getOffset();
}

private int moveRow(List<LadderRow> ladderRows, int columnPosition) {
for (LadderRow ladderRow : ladderRows) {
List<Boolean> points = ladderRow.getPoints();
columnPosition = moveColumn(points, columnPosition);
}
return columnPosition;
}

public List<PlayerResultDto> decidePlayerResults(List<Player> playerInventory) {
List<PlayerResultDto> playersInventory = new ArrayList<>();
for (Player player : playerInventory) {
int playerPosition = player.getPosition();
int columnPosition = moveRow(ladderRows, playerPosition);
String playerName = player.getName();
String playerResult = ladderResults.get(columnPosition);
playersInventory.add(new PlayerResultDto(playerName, playerResult));
}
return playersInventory;
}

public List<LadderRow> getLadderRows() {
return ladderRows;
}

public List<String> getLadderResults() {
return ladderResults;
}
}
17 changes: 17 additions & 0 deletions src/main/java/model/ladder/LadderMove.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package model.ladder;

public enum LadderMove {

Choose a reason for hiding this comment

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

이 부분까지 enum 으로 두기 쉽지 않았을텐데 분리하려고 열심히 하신 것이 보여서 되게 좋네요!

Copy link
Author

Choose a reason for hiding this comment

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

감사합니다!!
굳이 쓸 필요 없는 부분같긴 한 데 enum사용을 경험해본 다는 것에 의의를 두고 적어보았습니당 :)

RIGHT(1),
LEFT(-1),
DOWN(0);

private final int offset;

LadderMove(int offset) {
this.offset = offset;
}

public int getOffset() {
return offset;
}
}
16 changes: 16 additions & 0 deletions src/main/java/model/ladder/LadderPoint.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package model.ladder;

public enum LadderPoint {
CONNECTED(true),
DISCONNECTED(false);

private final Boolean isConnected;

LadderPoint(Boolean isConnected) {
this.isConnected = isConnected;
}

public Boolean getConnected() {
return isConnected;
}
}
32 changes: 32 additions & 0 deletions src/main/java/model/ladder/LadderRow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package model.ladder;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

public class LadderRow {
private List<Boolean> points = new ArrayList<>();

public LadderRow(int columnSize) {
for (int columnPosition = 0; columnPosition < columnSize; columnPosition++) {
boolean randomBoolean = chooseLadderPoint(columnPosition);
this.points.add(columnPosition, randomBoolean);
}
}

private LadderPoint randomConnectedOrDisconnected() {
if (ThreadLocalRandom.current().nextDouble() < 0.5) return LadderPoint.CONNECTED;
return LadderPoint.DISCONNECTED;
}

public boolean chooseLadderPoint(int columnPosition) {
if (columnPosition > 0 && points.get(columnPosition - 1))
return LadderPoint.DISCONNECTED.getConnected();
return randomConnectedOrDisconnected().getConnected();

}

public List<Boolean> getPoints() {
return points;
}
}
24 changes: 24 additions & 0 deletions src/main/java/model/player/Player.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package model.player;

public class Player {
private static final int NAME_LENGTH_LIMIT = 5;

Choose a reason for hiding this comment

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

자바 컨벤션 상으로 static 변수와 일반 변수는 1줄을 띄워야 할 것 같아요!

Copy link
Author

Choose a reason for hiding this comment

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

앗 static 변수와 일반 변수는 1줄을 띄어야 하는 군요! 수정하겠습니다!!
이런 디테일까지 알려주셔서 감사합니다 :)


private int position;
private String name;

public Player(int position, String name) {
if (name.length() > NAME_LENGTH_LIMIT) {
throw new IllegalArgumentException("참가자 이름이 " + NAME_LENGTH_LIMIT + "자 초과입니다.");
}
this.position = position;
this.name = name;
}

public int getPosition() {
return position;
}

public String getName() {
return name;
}
}
19 changes: 19 additions & 0 deletions src/main/java/model/player/PlayerResultDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package model.player;

public class PlayerResultDto {
private String playerName;
private String playerResult;

public PlayerResultDto(String playerName, String playerResult) {
this.playerName = playerName;
this.playerResult = playerResult;
}

public String getPlayerName() {
return playerName;
}

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

import java.util.List;
import java.util.Map;

public class PlayerResults {

Choose a reason for hiding this comment

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

바로 전에 말씀드린 Players 와는 반대로 이 클래스는 충분히 있어도 된다고 생각하는 편이긴 한데요
dto 의 경우에는 직접 Map<String, String> 형태로 넘기게 되면 이 클래스는 뭘 의미하는거지? 라는 것을 헷갈려 할 수도 있을 것 같아요
그래서 dto 에 경우에는 예외적으로 아무 메소드가 없어도 클래스로 만드는 것도 나쁘지 않다고 생각하는 편입니다!

Copy link
Author

@c0mpuTurtle c0mpuTurtle Nov 16, 2024

Choose a reason for hiding this comment

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

이번 기회를 통해 Map과 DTO의 차이점에 대해 알아보았습니다!


  1. 가독성
    DTO: 클래스와 필드로 구성되어 있으며, 필드가 어떤 형태의 데이터인 지, 어떤 데이터를 나타내는지 명확히 알 수 있다.
    Map: 키가 문자열이나 객체로 되어있어서 데이터의 구조가 복잡해지면 각 키의 의미를 파악하기 어렵다.

  2. 유연성
    DTO: 미리 정의된 클래스이므로, 데이터 구조를 변경하려면 DTO 클래스를 수정해야한다.
    Map: 동적으로 키를 추가하거나 삭제할 수 있어서 매우 유연하게 데이터를 다룰 수 있다.

  3. 타입 안정성
    DTO: 각 필드의 데이터 타입이 명확하게 지정되기 때문에 컴파일 타임에 타입 검사가 이루어져서 잘못된 타입의 데이터가 할당되면 에러를 발생시키므로 타입 안전성을 보장한다.
    Map : 값을 저장할 때 키와 관련된 타입 정보를 명시하지 않아도 되기 때문에 특정 키에 잘못된 타입의 값을 저장해도 컴파일 타임에서는 검사가 이루어지지 않으며, 런타임에 타입 에러가 발생할 가능성이 있다.

→ 결론:
데이터가 안전해야하고, 오류 검사가 빨라야하고, 고정된 데이터 구조가 필요할 때는 DTO를 사용하고
유연한 대처가 필요하고 데이터 구조가 고정되지 않았을 때는 Map을 사용하는 것이 좋다.


장단점을 비교해놓고 보니 확실히 프로그램이 커질수록 정보관리에는 DTO를 쓰는 게 더 적합하다는 생각이 듭니다!!
덕분에 Map과 DTO에 대해서 공부해볼 수 있는 기회가 생겼습니다! 감사합니다 :)

private List<PlayerResultDto> PlayerResultsInventory;

public PlayerResults(List<PlayerResultDto> playerResultsInventory) {
PlayerResultsInventory = playerResultsInventory;
}

public String getPlayerResult(String playerName) {
String result = null;
for (PlayerResultDto playerResultDto : PlayerResultsInventory) {
if (playerName.equals(playerResultDto.getPlayerName())) result = playerResultDto.getPlayerResult();
}
return result;
}

public List<PlayerResultDto> getPlayerResultsInventory() {
return PlayerResultsInventory;
}
}
21 changes: 21 additions & 0 deletions src/main/java/model/player/Players.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package model.player;

import model.tool.Splitter;

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

public class Players {

Choose a reason for hiding this comment

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

일급 컬렉션을 지키려고 노력하신 부분이 보여서 고생 많으셨다는 생각이 드네요
이 클래스의 경우에는 method 가 오직 getter 하나 뿐인데요 이렇게 되었을 경우에 일급 컬렉션의 역할을 하는 것 대신 클래스의 숫자가 늘어나서 코드를 읽기 힘들어질 수도 있을 것 같아요!
저는 개인적으로는 여기에 player 숫자에 대한 검증과 같은 무엇인가가 없다면 이 클래스는 없어도 된다는 생각입니다!

Copy link
Author

@c0mpuTurtle c0mpuTurtle Nov 15, 2024

Choose a reason for hiding this comment

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

< 질문 >

컨트롤러의 역할을 줄이기 위해 아래와 같이 Player객체 생성을 Controller가 아닌 Players가 하도록 로직을 변경해주었습니다.
Players의 생성자가 특별한 역할(객체 생성)을 수행하더라도, 여전히 method가 getter 하나뿐이라면 피드백과 동일한 의견인지 궁금합니다 ㅜㅠ
사실 저도 Players가 getter외에 다른 역할을 하지 않는다는 것을 인지하고 있었지만 그렇다면 Player의 모음체인 List<Player> PlayerInventory는 대체 어디서 관리해야하는지 감이 안 잡힙니다 ㅜㅠ

public Players(String [] playerNames){
        for (int i = 0; i < playerNames.length; i++) {
            Player player = new Player(i, playerNames[i]);
            playerInventory.add(player);
        }
    }

Copy link

@be-student be-student Nov 17, 2024

Choose a reason for hiding this comment

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

만약 그렇게 역할이 딱히 없다면 list 형태로 들고다녀도 저는 좋다고 생각합니다!
일단 지금의 코드를 기준으로 봤을 때, list 형태로 들고다니는 것과, 일급 컬렉션을 쓰는 것 그 2가지가 전혀 차이가 없다보니.... 오히려 일급컬렉션을 하다보면 코드 이해하기가 어려워지는 것 같아요

private List<Player> playerInventory= new ArrayList<>();

public Players(String [] playerNames){
for (int i = 0; i < playerNames.length; i++) {
Player player = new Player(i, playerNames[i]);
playerInventory.add(player);
}
}

public List<Player> getPlayerInventory() {
return playerInventory;
}
}
13 changes: 13 additions & 0 deletions src/main/java/model/tool/Splitter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package model.tool;

public class Splitter {
final static private String SPLIT_SIGN = ",";

public String[] splitWithComma(String beforeSplit) {
String[] afterSplit = beforeSplit.split(SPLIT_SIGN);
for (int i = 0; i < afterSplit.length; i++) {
afterSplit[i] = afterSplit[i].trim();
}
return afterSplit;
}
}
27 changes: 27 additions & 0 deletions src/main/java/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package view;

import java.util.Scanner;

public class InputView {
private static final Scanner scanner = new Scanner(System.in);

public static String inputPlayers(){
System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)");
return scanner.nextLine();
}

public static String inputResults(){
System.out.println("\n"+"실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)");
return scanner.nextLine();
}

public static Integer inputLadderHeight(){
System.out.println("\n"+"최대 사다리 높이는 몇 개인가요?");
return Integer.parseInt(scanner.nextLine());
}

public static String inputPlayerToWantResult(){
System.out.println("\n"+"결과를 보고 싶은 사람은?");
return scanner.nextLine();
}
}
Loading