-
Notifications
You must be signed in to change notification settings - Fork 743
2단계 - 사다리(생성) #2431
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
Merged
Merged
2단계 - 사다리(생성) #2431
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
0015006
docs: 2단계 사다리 생성 문서 추가 및 README 링크 수정
ghtjr410 e71ce28
feat: Name 값 객체 추가 및 유효성 검증 테스트 작성
ghtjr410 d57f6b7
feat: 이름 길이 검증 로직 추가 및 테스트 보강
ghtjr410 33a451d
feat: Names 컬렉션 값 객체 추가 및 파싱 테스트 구현
ghtjr410 8e4bb20
feat: Names 최소 인원 검증 로직 추가 및 예외 테스트 보강
ghtjr410 3803316
test: Name 도메인에 정상 입력 테스트 케이스 추가
ghtjr410 377505e
feat: Line 도메인 추가 및 검증 테스트 구현
ghtjr410 5ec5b06
feat: Line 입력 검증 강화 및 빈값 테스트 추가
ghtjr410 ffa6d71
feat: Height 도메인 생성 및 최소 높이 검증 추가
ghtjr410 5c0728a
feat: Lines 및 LineGenerator 도메인 추가
ghtjr410 d3f9217
feat: Line 출력 기능 추가
ghtjr410 4ad601b
feat: Lines 출력 기능 추가
ghtjr410 84d6ded
feat: 이름/참가자 목록 출력용 toDisplay 기능 추가
ghtjr410 bb95a11
refactor: 사다리 줄(Line) 출력 포맷 개선
ghtjr410 44dc2c7
feat: Ladder 도메인 생성 및 전체 출력(toDisplay) 기능 추가
ghtjr410 1c43edc
feat: 랜덤 사다리 라인 생성 로직 및 테스트 추가
ghtjr410 768993e
feat: 사다리 게임 실행용 Application 및 입출력 뷰 추가
ghtjr410 ebdfe80
docs: 사다리 생성 기능 전체 구현 목록 문서 업데이트
ghtjr410 0b4fd6a
fix: 이름 길이 검증 시 하드코딩 제거
ghtjr410 a548930
docs: PR 링크 추가
ghtjr410 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| # 2단계 - 사다리(생성) | ||
| *** | ||
| ## 코드 리뷰 | ||
| > PR 링크: | ||
| > **[https://github.com/next-step/java-ladder/pull/2431](https://github.com/next-step/java-ladder/pull/2431)** | ||
|
|
||
| ## 기능 요구사항 | ||
| - 사다리 게임에 참여하는 사람에 이름을 최대5글자까지 부여할 수 있다. 사다리를 출력할 때 사람 이름도 같이 출력한다. | ||
| - 사람 이름은 쉼표(,)를 기준으로 구분한다. | ||
| - 사람 이름을 5자 기준으로 출력하기 때문에 사다리 폭도 넓어져야 한다. | ||
| - 사다리 타기가 정상적으로 동작하려면 라인이 겹치지 않도록 해야 한다. | ||
| - |-----|-----| 모양과 같이 가로 라인이 겹치는 경우 어느 방향으로 이동할지 결정할 수 없다. | ||
|
|
||
| ## 프로그래밍 요구사항 | ||
| - **자바 8의 스트림과 람다를 적용해 프로그래밍한다.** | ||
| - **규칙 6: 모든 엔티티를 작게 유지한다.** | ||
|
|
||
| ### 실행 결과 | ||
| - 위 요구사항에 따라 4명의 사람을 위한 5개 높이 사다리를 만들 경우, 프로그램을 실행한 결과는 다음과 같다. | ||
| ```text | ||
| 참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요) | ||
| pobi,honux,crong,jk | ||
|
|
||
| 최대 사다리 높이는 몇 개인가요? | ||
| 5 | ||
|
|
||
| 실행결과 | ||
|
|
||
| pobi honux crong jk | ||
| |-----| |-----| | ||
| | |-----| | | ||
| |-----| | | | ||
| | |-----| | | ||
| |-----| |-----| | ||
| ``` | ||
|
|
||
| ### 힌트 | ||
| - 2차원 배열을 ArrayList, Generic을 적용해 구현하면 ArrayList<ArrayList<Boolean>>와 같이 이해하기 어려운 코드가 추가된다. | ||
| - 사다리 게임에서 한 라인의 좌표 값을 가지는 객체를 추가해 구현해 본다. | ||
| ```java | ||
| public class Line { | ||
| private List<Boolean> points = new ArrayList<>(); | ||
|
|
||
| public Line (int countOfPerson) { | ||
| // 라인의 좌표 값에 선이 있는지 유무를 판단하는 로직 추가 | ||
| } | ||
|
|
||
| [...] | ||
| } | ||
| ``` | ||
| - 위와 같이 Line 객체를 추가하면 ArrayList<ArrayList<Boolean>> 코드를 ArrayList<Line>과 같이 구현하는 것이 가능해 진다. | ||
|
|
||
| ## PR 전 점검 | ||
| **[체크리스트 확인하기](checklist.md)** | ||
|
|
||
| ## 구현 기능 목록 | ||
|
|
||
| ### 참가자 이름 | ||
| - [x] 이름 (Name) | ||
| - [x] 문자열로 생성 | ||
| - [x] null 또는 빈 값일 시 예외 발생 | ||
| - [x] 5자 초과 시 예외 발생 | ||
| - [x] 6자 고정 출력용 문자열 반환 | ||
|
|
||
| - [x] 이름 목록 (Names) | ||
| - [x] 쉼표 구분 문자열로 생성 | ||
| - [x] 이름 목록으로 생성 | ||
| - [x] null일 시 예외 발생 | ||
| - [x] 2명 미만일 시 예외 발생 | ||
| - [x] 참가자 수 반환 | ||
| - [x] 출력용 문자열 반환 | ||
|
|
||
| ### 사다리 높이 | ||
| - [x] 높이 (Height) | ||
| - [x] 정수로 생성 | ||
| - [x] 1 미만일 시 예외 발생 | ||
|
|
||
| ### 사다리 라인 | ||
| - [x] 라인 (Line) | ||
| - [x] Boolean 목록으로 생성 | ||
| - [x] 가변인자로 생성 | ||
| - [x] null 또는 빈 값일 시 예외 발생 | ||
| - [x] 연속된 true 존재 시 예외 발생 | ||
| - [x] 출력용 문자열 반환 | ||
|
|
||
| - [x] 라인 목록 (Lines) | ||
| - [x] Line 목록으로 생성 | ||
| - [x] Height, 참가자 수, LineGenerator로 생성 | ||
| - [x] 출력용 문자열 반환 | ||
|
|
||
| ### 사다리 생성 | ||
| - [x] 라인 생성기 인터페이스 (LineGenerator) | ||
| - [x] Line 생성 메서드 정의 | ||
|
|
||
| - [x] Boolean 생성기 인터페이스 (BooleanGenerator) | ||
| - [x] boolean 생성 메서드 정의 | ||
|
|
||
| - [x] 랜덤 라인 생성기 (RandomLineGenerator) | ||
| - [x] LineGenerator 구현 | ||
| - [x] BooleanGenerator 주입 가능 | ||
| - [x] 기본 생성 시 Random 사용 | ||
| - [x] 연속 true 방지 로직 적용 | ||
|
|
||
| ### 사다리 | ||
| - [x] 사다리 (Ladder) | ||
| - [x] Names, Lines로 생성 | ||
| - [x] 출력용 문자열 반환 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # 미션 제출 전 체크리스트 | ||
|
|
||
| ### 1. 객체지향 생활체조 원칙 | ||
| - 규칙 1: 한 메서드에 오직 한 단계의 들여쓰기(indent)만 사용 | ||
| - 규칙 2: else 예약어 사용 금지 | ||
| - 규칙 3: 모든 원시값과 문자열을 포장 (Primitive Obsession 제거) | ||
| - 규칙 4: 한 줄에 점(.) 하나만 사용 (Law of Demeter) | ||
| - 규칙 5: 축약 금지 (명확한 네이밍) | ||
| - 규칙 6: 모든 엔티티 작게 유지 | ||
| - 규칙 7: 3개 이상의 인스턴스 변수를 가진 클래스 지양 | ||
| - 규칙 8: 일급 콜렉션 사용 | ||
| - 규칙 9: Getter/Setter 사용 지양 (Tell, Don't Ask) | ||
|
|
||
| ### 2. 메서드 작성 원칙 | ||
| - 한 가지 일만 수행하는 작은 메서드 (최대 10라인) | ||
|
|
||
| ### 2. 객체지향 설계 품질 | ||
| - 가장 작은 단위의 객체부터 시작 (상향식 개발) | ||
| - 객체에게 상태 노출 대신 책임 위임 | ||
| - 불변성을 최대한 유지 | ||
| - 도메인 용어로 네이밍 | ||
| - 협력 구조가 자연스럽게 읽힘 | ||
|
|
||
| ### 3. TDD | ||
| - TDD 사이클(Red-Green-Refactor)로 구현 | ||
| - TDD 사이클 단위 커밋 | ||
|
|
||
| ### 4. 테스트 품질 | ||
| - 객체의 책임과 결과만 검증 (구현 세부사항 테스트 지양) | ||
| - 단순 위임은 테스트 생략, 조건·분기·조합이 있다면 테스트 | ||
| - 테스트 객체 생성 헬퍼메서드가 반복되면 부생성자 도입 검토 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package nextstep.ladder; | ||
|
|
||
| import nextstep.ladder.domain.*; | ||
| import nextstep.ladder.view.InputView; | ||
| import nextstep.ladder.view.OutputView; | ||
|
|
||
| public class Application { | ||
| public static void main(String[] args) { | ||
| Names names = new Names(InputView.readNames()); | ||
| Height height = new Height(InputView.readHeight()); | ||
|
|
||
| Lines lines = new Lines(height, names.size(), new RandomLineGenerator()); | ||
| Ladder ladder = new Ladder(names, lines); | ||
|
|
||
| OutputView.printResult(ladder); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package nextstep.ladder.domain; | ||
|
|
||
| @FunctionalInterface | ||
| public interface BooleanGenerator { | ||
| boolean generate(); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package nextstep.ladder.domain; | ||
|
|
||
| public record Height(int value) { | ||
| private static final int MIN_HEIGHT = 1; | ||
|
|
||
| public Height { | ||
| if (value < MIN_HEIGHT) { | ||
| throw new IllegalArgumentException("높이는 %d이상이어야 합니다. 입력값: %d".formatted(MIN_HEIGHT, value)); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package nextstep.ladder.domain; | ||
|
|
||
| public record Ladder(Names names, Lines lines) { | ||
|
|
||
| public String toDisplay() { | ||
| return names.toDisplay() + "\n" + lines.toDisplay(); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package nextstep.ladder.domain; | ||
|
|
||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
| import java.util.stream.IntStream; | ||
|
|
||
| public record Line(List<Boolean> points) { | ||
| private static final String CONNECTED = "-----|"; | ||
| private static final String EMPTY = " |"; | ||
|
|
||
| public Line(Boolean... inputs) { | ||
| this(List.of(inputs)); | ||
| } | ||
|
|
||
| public Line { | ||
| validate(points); | ||
| points = List.copyOf(points); | ||
| } | ||
|
|
||
| private void validate(List<Boolean> inputs) { | ||
| if (inputs == null || inputs.isEmpty()) { | ||
| throw new IllegalArgumentException("가로선 정보는 필수입니다."); | ||
| } | ||
|
|
||
| boolean hasConsecutive = | ||
| IntStream.range(0, inputs.size() - 1).anyMatch(i -> inputs.get(i) && inputs.get(i + 1)); | ||
|
|
||
| if (hasConsecutive) { | ||
| throw new IllegalArgumentException("가로선은 연속될 수 없습니다."); | ||
| } | ||
| } | ||
|
|
||
| public String toDisplay() { | ||
| String body = points.stream().map(this::toSegment).collect(Collectors.joining()); | ||
| return " |" + body; | ||
| } | ||
|
|
||
| private String toSegment(boolean connected) { | ||
| return connected ? CONNECTED : EMPTY; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package nextstep.ladder.domain; | ||
|
|
||
| @FunctionalInterface | ||
| public interface LineGenerator { | ||
| Line generate(int count); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package nextstep.ladder.domain; | ||
|
|
||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
| import java.util.stream.IntStream; | ||
|
|
||
| public record Lines(List<Line> values) { | ||
|
|
||
| public Lines(int height, int personCount, LineGenerator generator) { | ||
|
Contributor
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. 인터페이스 기반으로 개발하고 DI를 통해 주입함으로써 테스트 가능한 구조로 설계 👍 |
||
| this(new Height(height), personCount, generator); | ||
| } | ||
|
|
||
| public Lines(Height height, int personCount, LineGenerator generator) { | ||
| this(generate(height, personCount, generator)); | ||
| } | ||
|
|
||
| private static List<Line> generate(Height height, int personCount, LineGenerator generator) { | ||
| return IntStream.range(0, height.value()) | ||
| .mapToObj(i -> generator.generate(personCount - 1)) | ||
| .toList(); | ||
| } | ||
|
|
||
| public Lines(List<Line> values) { | ||
| this.values = List.copyOf(values); | ||
| } | ||
|
|
||
| public String toDisplay() { | ||
| return values.stream().map(Line::toDisplay).collect(Collectors.joining("\n")); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package nextstep.ladder.domain; | ||
|
|
||
| public record Name(String value) { | ||
| private static final int MAX_LENGTH = 5; | ||
|
|
||
| public Name { | ||
| if (value == null || value.isBlank()) { | ||
| throw new IllegalArgumentException("이름은 필수입니다. 입력값: " + value); | ||
| } | ||
|
|
||
| if (value.length() > MAX_LENGTH) { | ||
| throw new IllegalArgumentException("이름은 %d자 이하여야 합니다. 입력값: %s".formatted(MAX_LENGTH, value)); | ||
| } | ||
| } | ||
|
|
||
| public String toDisplay() { | ||
| return String.format("%-6s", value); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package nextstep.ladder.domain; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.List; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| public record Names(List<Name> values) { | ||
| private static final int MIN_SIZE = 2; | ||
|
|
||
| public Names(String input) { | ||
| this(Arrays.stream(input.split(",")).map(Name::new).toList()); | ||
| } | ||
|
|
||
| public Names(List<Name> values) { | ||
| validate(values); | ||
|
|
||
| this.values = List.copyOf(values); | ||
| } | ||
|
|
||
| private void validate(List<Name> values) { | ||
| if (values == null) { | ||
| throw new IllegalArgumentException("참가자는 필수입니다."); | ||
| } | ||
|
|
||
| if (values.size() < MIN_SIZE) { | ||
| throw new IllegalArgumentException("참가자는 %d명 이상이어야 합니다. 입력 인원: %d".formatted(MIN_SIZE, values.size())); | ||
| } | ||
| } | ||
|
|
||
| public int size() { | ||
| return values.size(); | ||
| } | ||
|
|
||
| public String toDisplay() { | ||
| return values.stream().map(Name::toDisplay).collect(Collectors.joining()); | ||
| } | ||
| } |
30 changes: 30 additions & 0 deletions
30
src/main/java/nextstep/ladder/domain/RandomLineGenerator.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package nextstep.ladder.domain; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Random; | ||
|
|
||
| public class RandomLineGenerator implements LineGenerator { | ||
| private final BooleanGenerator booleanGenerator; | ||
|
|
||
| public RandomLineGenerator() { | ||
| this(new Random()::nextBoolean); | ||
| } | ||
|
|
||
| public RandomLineGenerator(BooleanGenerator booleanGenerator) { | ||
| this.booleanGenerator = booleanGenerator; | ||
| } | ||
|
|
||
| @Override | ||
| public Line generate(int count) { | ||
| List<Boolean> points = new ArrayList<>(); | ||
|
|
||
| for (int i = 0; i < count; i++) { | ||
| boolean prev = i > 0 && points.get(i - 1); | ||
| boolean current = !prev && booleanGenerator.generate(); | ||
| points.add(current); | ||
| } | ||
|
|
||
| return new Line(points); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| package nextstep.ladder.view; | ||
|
|
||
| import java.util.Scanner; | ||
|
|
||
| public class InputView { | ||
| private static final Scanner SCANNER = new Scanner(System.in); | ||
|
|
||
| public static String readNames() { | ||
| System.out.println("참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); | ||
| return SCANNER.nextLine(); | ||
| } | ||
|
|
||
| public static int readHeight() { | ||
| System.out.println("최대 사다리 높이는 몇 개인가요?"); | ||
| return Integer.parseInt(SCANNER.nextLine()); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| package nextstep.ladder.view; | ||
|
|
||
| import nextstep.ladder.domain.Ladder; | ||
|
|
||
| public class OutputView { | ||
|
|
||
| public static void printResult(Ladder ladder) { | ||
| System.out.println("\n실행결과\n"); | ||
| System.out.println(ladder.toDisplay()); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
👍