-
Notifications
You must be signed in to change notification settings - Fork 12
[사다리 타기] 김이화 미션 제출합니다. #6
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: ihwag719
Are you sure you want to change the base?
Changes from all commits
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 |
|---|---|---|
|
|
@@ -30,3 +30,4 @@ out/ | |
|
|
||
| ### VS Code ### | ||
| .vscode/ | ||
| .DS_STORE | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| 1. 참여할 사람 이름 입력 -> 조건 : 사람 최소 2명, 이름은 쉼표(,)로 구분, 이름 최대 5글자, 이름 빈칸 X | ||
| 2. 사다리 게임 높이 입력 -> 조건 : 최소 높이 2이상, 높이 숫자만 입력 | ||
| 3. 사다리 게임 -> 조건 : 사람 | ||
| 4. 실행 결과: 사람 이름, 사다리 동시 출력, 사람 이름 5글자 맞춰서 출력, 라인 겹치지 않게 출력 | ||
|
|
||
| - 모든 기능을 TDD로 구현해 단위 테스트가 존재해야 한다. 단, UI(System.out, System.in) 로직은 제외 | ||
| - 핵심 로직을 구현하는 코드와 UI를 담당하는 로직을 구분한다. | ||
| - UI 로직을 InputView, ResultView와 같은 클래스를 추가해 분리한다. | ||
| - 함수(또는 메서드)의 길이가 10라인을 넘어가지 않도록 구현한다. | ||
| - 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. | ||
| - 배열 대신 컬렉션을 사용한다. | ||
| - Java Enum을 적용한다. | ||
| - 모든 원시 값과 문자열을 포장한다. | ||
| - 줄여 쓰지 않는다(축약 금지). | ||
| - 일급 컬렉션을 쓴다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import controller.GameController; | ||
| import view.InputView; | ||
| import view.OutPutView; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| InputView inputView = new InputView(); | ||
| OutPutView outPutView = new OutPutView(); | ||
| GameController gameController = new GameController(inputView,outPutView); | ||
| gameController.playGame(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package controller; | ||
|
|
||
| import static java.util.stream.Collectors.collectingAndThen; | ||
| import static java.util.stream.Collectors.toList; | ||
| import java.util.List; | ||
| import model.Ladder; | ||
| import model.User; | ||
| import model.Users; | ||
| import view.InputView; | ||
| import view.OutPutView; | ||
|
|
||
| public class GameController { | ||
|
|
||
| private final InputView inputView; | ||
| private final OutPutView outPutView; | ||
|
|
||
| public GameController(InputView inputView, OutPutView outPutView) { | ||
| this.inputView = inputView; | ||
| this.outPutView = outPutView; | ||
| } | ||
|
|
||
| public void playGame() { | ||
| Users users = createUsers(); | ||
| int ladderHeight = inputView.createLadderHeight(); | ||
| Ladder ladder = new Ladder(users.getUserCount(), ladderHeight); | ||
| outPutView.gameResult(users, ladder); | ||
|
|
||
| } | ||
|
|
||
| private Users createUsers() { | ||
| List<String> userNames = inputView.createUser(); | ||
|
|
||
| return userNames.stream() | ||
| .map(i -> new User(i)) | ||
| .collect(collectingAndThen(toList(), i -> new Users(i))); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| package model; | ||
|
|
||
| public enum Direction { | ||
| RIGHT, NEUTRAL, LEFT | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package model; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| public class Ladder { | ||
| private static final int MIN_LADDER_HEIGHT = 2; | ||
| private final List<Line> lines; | ||
|
|
||
| public Ladder(int userCount, int height) { | ||
| validateHeight(height); | ||
|
|
||
| this.lines = new ArrayList<>(); | ||
|
|
||
| for (int i = 0; i < height; i++) { | ||
| lines.add(new Line(userCount)); | ||
| } | ||
| } | ||
|
|
||
| private void validateHeight(int height) { | ||
| if (height < MIN_LADDER_HEIGHT) { | ||
| throw new IllegalArgumentException("사다리 높이는 최소 2이상이어야 합니다."); | ||
| } | ||
| } | ||
|
|
||
| public List<Line> getLines(){ | ||
| return this.lines; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| package model; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| import java.util.Random; | ||
|
|
||
| public class Line { | ||
|
|
||
| private final Random random = new Random(); | ||
|
|
||
| private final List<Direction> points; | ||
|
|
||
| public Line(int personCount) { | ||
|
|
||
| points = new ArrayList<>(); | ||
| createLine(personCount,points); | ||
|
Comment on lines
+16
to
+17
Collaborator
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. 코드를 읽으면서 이 부분이 좀 의아했습니다. 또한 createLine()은 메서드 내부에서 값을 변경하고 있습니다. points = new ArrayList<>();
createLine(personCount, points);이거보다 points = createLine(personCount);이게 훨씬 이해하기 쉬워 보이네요. |
||
| } | ||
|
|
||
| private void createLine(int personCount, List<Direction> points){ | ||
| Direction previousDirection = Direction.NEUTRAL; | ||
|
|
||
| for (int i = 0; i < personCount - 1; i++) { | ||
| Direction newDirection = createDirection(previousDirection); | ||
| points.add(newDirection); | ||
| previousDirection = updatePreviousDirection(previousDirection, newDirection); | ||
| } | ||
| // 마지막 포인트는 항상 NEUTRAL | ||
| points.add(Direction.NEUTRAL); | ||
| } | ||
|
|
||
| private Direction createDirection(Direction previousDirection) { | ||
| if (previousDirection == Direction.LEFT) { | ||
| return Direction.NEUTRAL; | ||
| } | ||
| if (random.nextBoolean()) { | ||
| return Direction.RIGHT; | ||
| } | ||
| return Direction.NEUTRAL; | ||
| } | ||
|
|
||
| private Direction updatePreviousDirection(Direction previousDirection, Direction newDirection) { | ||
| // 오른쪽으로 가는 경우 다음 포인트는 왼쪽으로 갈 수 없습니다. | ||
| if (newDirection == Direction.RIGHT) { | ||
| return previousDirection = Direction.LEFT; | ||
| } | ||
| return previousDirection = Direction.NEUTRAL; | ||
|
Comment on lines
+42
to
+47
Collaborator
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. 여기서 첫째 파라미터인 previousDirection은 없어도 될 것 같아요! |
||
| } | ||
|
Comment on lines
+20
to
+48
Collaborator
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.
천천히 읽으면서 분석해야 알 수 있는 부분이 많아서 다른 구현 예시를 보면서 좀 더 단순하게 만들 수 있을지 고민해보거나, |
||
|
|
||
| public List<Direction> getPoints() { | ||
| return this.points; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package model; | ||
|
|
||
| public class User { | ||
| private static final int MAX_NAME_LENGTH = 5; | ||
| private static final String BLANK = " "; | ||
|
|
||
| private final String userName; | ||
|
|
||
| public User(String userName) { | ||
| validateUserName(userName); | ||
| this.userName = userName; | ||
| } | ||
|
|
||
| private void validateUserName(String name) { | ||
| validateNameLength(name); | ||
| validateBlankInName(name); | ||
| } | ||
|
|
||
| private void validateNameLength(String name){ | ||
| if(name.isEmpty() || name.length() > MAX_NAME_LENGTH){ | ||
| throw new IllegalArgumentException("유저 이름은 1~5자리 글자여야 합니다."); | ||
| } | ||
| } | ||
|
|
||
| private void validateBlankInName(String name){ | ||
| if(name.contains(BLANK)){ | ||
| throw new IllegalArgumentException("유저 이름은 빈칸이 들어갈 수 없습니다."); | ||
| } | ||
| } | ||
|
|
||
| public String getUserName(){ | ||
| return this.userName; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| package model; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class Users { | ||
| private static final int MIN_USERS = 2; | ||
|
|
||
| private final List<User> users; | ||
|
|
||
| public Users(final List<User> users) { | ||
| validateUsersSizeValidate(users); | ||
| this.users = users; | ||
| } | ||
|
|
||
| private void validateUsersSizeValidate(List<User> users) { | ||
| validateUsers(users); | ||
| } | ||
|
|
||
| private void validateUsers(List<User> users) { | ||
| if (users.size() < MIN_USERS) { | ||
| throw new IllegalArgumentException("유저는 2명 이상이어야 합니다."); | ||
| } | ||
| } | ||
|
|
||
| public int getUserCount() { | ||
| return users.size(); | ||
| } | ||
|
|
||
| public List<String> getUserName() { | ||
| return users.stream().map(i -> i.getUserName()).toList(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| package view; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.Scanner; | ||
|
|
||
| public class InputView { | ||
|
|
||
| private static final String separation = ","; | ||
|
|
||
| private Scanner sc = new Scanner(System.in); | ||
|
|
||
| public List<String> createUser(){ | ||
| System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); | ||
| String name = sc.nextLine(); | ||
| validateCreateUser(name); | ||
|
|
||
| return Arrays.stream(name.split(separation)).toList(); | ||
| } | ||
|
|
||
| public int createLadderHeight(){ | ||
| System.out.println("최대 사다리 높이는 몇 개인가요?"); | ||
| int ladderHeight = sc.nextInt(); | ||
| validateCreateHeight(ladderHeight); | ||
|
|
||
| return ladderHeight; | ||
| } | ||
|
|
||
| private void validateCreateUser(String name){ | ||
| if(name.isEmpty()||name.endsWith(separation)){ | ||
| throw new IllegalArgumentException("잘못된 입력입니다."); | ||
| } | ||
| } | ||
|
|
||
| private void validateCreateHeight(int ladderHeight){ | ||
| if(ladderHeight<2){ | ||
| throw new IllegalArgumentException("2이상의 숫자만 입력하세요"); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package view; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import model.Direction; | ||
| import model.Ladder; | ||
| import model.Line; | ||
| import model.Users; | ||
|
|
||
| public class OutPutView { | ||
|
|
||
| private static final int MAX_NAME = 5; | ||
| private static final String BLANK = " "; | ||
| private static final String POINT = "-----"; | ||
| private static final String LINE = "|"; | ||
|
|
||
| public void gameResult(Users users, Ladder ladder) { | ||
| System.out.println("실행결과"); | ||
| printUserNames(users); | ||
| printLadder(ladder); | ||
| } | ||
|
|
||
| private void printUserNames(Users users) { | ||
| StringBuilder sb = new StringBuilder(); | ||
| for (String name : users.getUserName()) { | ||
| remakeNameFormat(sb, name); | ||
| } | ||
| System.out.println(sb); | ||
| } | ||
|
|
||
| private void remakeNameFormat(StringBuilder sb, String name) { | ||
| int blankNum = MAX_NAME - name.length(); | ||
| if (name.length() < MAX_NAME) { | ||
| sb.append(BLANK.repeat(blankNum)); | ||
| } | ||
| sb.append(name).append(BLANK); | ||
| } | ||
|
|
||
| private void printLadder(Ladder ladder) { | ||
| List<Line> lineList = ladder.getLines(); | ||
| StringBuilder sb = new StringBuilder(); | ||
| for (Line line : lineList) { | ||
| printLadderPoint(sb, line.getPoints()); | ||
| } | ||
| System.out.println(sb); | ||
| } | ||
|
|
||
| private void printLadderPoint(StringBuilder sb, List<Direction> directions) { | ||
| sb.append(BLANK.repeat(MAX_NAME)); | ||
| for (int i = 0; i < directions.size() - 1; i++) { | ||
| sb.append(LINE) | ||
| .append(printDirections(directions.get(i))); | ||
| } | ||
| sb.append(LINE).append(System.lineSeparator()); | ||
| } | ||
|
|
||
| private String printDirections(Direction direction) { | ||
| if (direction == Direction.RIGHT) { | ||
| return POINT; | ||
| } | ||
| return BLANK.repeat(MAX_NAME); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package ladder; | ||
|
|
||
| import model.Ladder; | ||
| import model.Line; | ||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
|
|
||
| public class LadderTest { | ||
|
Collaborator
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. 테스트 클래스는 public이 아니어도 괜찮습니다. |
||
| @DisplayName("주어진 높이에 맞게 사다리 생성") | ||
| @Test | ||
| void newLadderTest() { | ||
| int userCount = 5; | ||
| int height = 6; | ||
|
|
||
| Ladder ladder = new Ladder(userCount, height); | ||
| List<Line> lines = ladder.getLines(); | ||
| assertEquals(height, lines.size()); | ||
| } | ||
|
|
||
| @DisplayName("사다리의 높이가 2개 미만일 경우 예외 발생") | ||
| @Test | ||
| void ValidateLadderHeightTest() { | ||
| int userCount = 5; | ||
| int height = 1; | ||
|
|
||
| assertThatThrownBy(() -> new Ladder(userCount, height)) | ||
| .isInstanceOf(IllegalArgumentException.class) | ||
| .hasMessage("사다리 높이는 최소 2이상이어야 합니다."); | ||
| } | ||
| } | ||
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.
필수는 아니지만, 메서드 참조식의 사용도 고려해볼 만 합니다.
User::new로도 '스트림의 원소를 User 생성자의 유일한 파라미터로 넣어 생성자를 호출함'을 알 수 있으니까요!