-
Notifications
You must be signed in to change notification settings - Fork 55
[BCSD Lab] 박태진, 4차시 미션 제출합니다 #74
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
Changes from all commits
1871a08
81977ed
18f72bc
6642ea6
a0c8d64
c55b431
b0d6430
761f666
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 |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Random; | ||
import java.util.Scanner; | ||
|
||
import domain.Columns; | ||
import domain.Ladder; | ||
import domain.LadderHeight; | ||
import domain.Participants; | ||
import domain.RungLength; | ||
import service.LadderNavigator; | ||
import service.RandomConnectionGenerator; | ||
import view.LadderRenderer; | ||
|
||
public class App { | ||
public static void main(String[] args) { | ||
try (Scanner scanner = new Scanner(System.in)) { | ||
Participants participants = readParticipants(scanner); | ||
List<String> labels = readLabels(scanner, participants.size()); | ||
int heightCount = readHeight(scanner); | ||
Ladder ladder = buildLadder(participants.size(), heightCount); | ||
renderLadder(participants.names(), labels, ladder); | ||
queryAndPrintResults(scanner, participants, labels, ladder); | ||
} | ||
} | ||
|
||
private static Participants readParticipants(Scanner scanner) { | ||
while (true) { | ||
List<String> names = readCommaSeparated(scanner, "참여할 사람 이름을 입력하세요. (이름은 쉼표(,)로 구분하세요)"); | ||
try { return Participants.of(names); } | ||
catch (IllegalArgumentException e) { System.out.println(e.getMessage()); } | ||
} | ||
} | ||
|
||
private static List<String> readLabels(Scanner scanner, int count) { | ||
while (true) { | ||
List<String> labels = readCommaSeparated(scanner, "실행 결과를 입력하세요. (결과는 쉼표(,)로 구분하세요)"); | ||
if (labels.size() != count) { System.out.println("결과의 개수는 참여자 수(" + count + ")와 같아야 합니다"); continue; } | ||
return labels; | ||
} | ||
} | ||
|
||
private static List<String> readCommaSeparated(Scanner scanner, String prompt) { | ||
System.out.println(prompt); | ||
return Arrays.stream(scanner.nextLine().trim().split(",")) | ||
.map(String::trim) | ||
.filter(s -> !s.isEmpty()) | ||
.toList(); | ||
} | ||
|
||
private static int readHeight(Scanner scanner) { | ||
System.out.println(); | ||
System.out.println("최대 사다리 높이는 몇 개인가요?"); | ||
return Integer.parseInt(scanner.nextLine().trim()); | ||
} | ||
|
||
private static Ladder buildLadder(int numNames, int heightCount) { | ||
Columns columns = Columns.of(numNames); | ||
LadderHeight height = LadderHeight.of(heightCount); | ||
RandomConnectionGenerator generator = new RandomConnectionGenerator(new Random()); | ||
return generator.generateLadder(height, columns); | ||
} | ||
|
||
private static void renderLadder(List<String> names, List<String> labels, Ladder ladder) { | ||
Columns columns = Columns.of(names.size()); | ||
RungLength rungLength = RungLength.defaultFive(); | ||
LadderRenderer renderer = new LadderRenderer(); | ||
System.out.println("\n사다리 결과\n"); | ||
System.out.println(renderer.renderNamesHeader(names, rungLength)); | ||
renderer.print(ladder, columns, rungLength); | ||
System.out.println(renderer.renderBottomLabels(labels, rungLength)); | ||
} | ||
|
||
private static void queryAndPrintResults(Scanner scanner, Participants participants, List<String> labels, Ladder ladder) { | ||
Columns columns = Columns.of(participants.size()); | ||
LadderNavigator navigator = new LadderNavigator(); | ||
while (true) { | ||
String who = promptWho(scanner); | ||
if (handleSelection(navigator, ladder, columns, participants, labels, who)) break; | ||
} | ||
} | ||
|
||
private static String promptWho(Scanner scanner) { | ||
System.out.println(); | ||
System.out.println("결과를 보고 싶은 사람은?"); | ||
String who = scanner.nextLine().trim(); | ||
System.out.println("\n실행 결과"); | ||
return who; | ||
} | ||
|
||
private static boolean handleSelection( | ||
LadderNavigator navigator, | ||
Ladder ladder, | ||
Columns columns, | ||
Participants participants, | ||
List<String> labels, | ||
String who | ||
) { | ||
if (who.equalsIgnoreCase("all")) { handleAllRequest(navigator, ladder, columns, participants, labels); return true; } | ||
int index = participants.indexOf(who); | ||
if (index < 0) { System.out.println("존재하지 않는 이름입니다"); return false; } | ||
printSingleResult(navigator, ladder, columns, index, labels); | ||
return false; | ||
} | ||
|
||
private static void handleAllRequest(LadderNavigator navigator, Ladder ladder, Columns columns, Participants participants, List<String> labels) { | ||
printAllResults(navigator, ladder, columns, participants, labels); | ||
} | ||
|
||
private static void printAllResults(LadderNavigator navigator, Ladder ladder, Columns columns, Participants participants, List<String> labels) { | ||
for (int i = 0; i < columns.count(); i++) { printSingleMapping(navigator, ladder, columns, participants, labels, i); } | ||
} | ||
|
||
private static void printSingleResult(LadderNavigator navigator, Ladder ladder, Columns columns, int index, List<String> labels) { | ||
int dest = navigator.traverse(ladder, columns, index); | ||
System.out.println(labels.get(dest)); | ||
} | ||
|
||
private static void printSingleMapping(LadderNavigator navigator, Ladder ladder, Columns columns, Participants participants, List<String> labels, int i) { | ||
int dest = navigator.traverse(ladder, columns, i); | ||
String name = participants.get(i); | ||
String label = labels.get(dest); | ||
System.out.println(name + " : " + label); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package domain; | ||
|
||
public final class Columns { | ||
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. final class로 만든 이유가 있을까요? 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. 열의 개수는 최소 2개라는 불변 조건을 지켜야 하므로, 이 조건을 항상 만족할 수 있도록 상속을 막는 final을 사용했습니다! 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.
동일 |
||
private final int count; | ||
|
||
private Columns(int count) { | ||
this.count = count; | ||
} | ||
|
||
public static Columns of(int count) { | ||
if (count < 2) { | ||
throw new IllegalArgumentException("열의 개수는 2 이상이어야 합니다"); | ||
} | ||
return new Columns(count); | ||
} | ||
|
||
public static Columns fixedFour() { | ||
return of(4); | ||
} | ||
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. 사다리 크기를 입력 받아 생성하기 전에 쓰던 코드가 남아있는 것 같아요. 상수를 이용해 Columns를 구성할 경우, 해당 상수는 Columns를 생성하는 주체 측에 위치하는 것이 바람직하다고 생각해요. 테스트 코드를 작성하면서 fixture를 정의할 때 Columns 클래스 내부에 fixture를 작성하지 않는 것처럼요. 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. 말씀해 주신 것처럼 Columns 에서 검증만 맡고 4같은 상수는 생성하는 주체에 있는 것이 올바른 것 같습니다. |
||
|
||
public int count() { | ||
return count; | ||
} | ||
|
||
public int numberOfSegments() { | ||
return count - 1; | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package domain; | ||
|
||
public enum Connection { | ||
CONNECTED, | ||
EMPTY | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package domain; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
public final class Ladder { | ||
private final List<Row> rows; | ||
|
||
private Ladder(List<Row> rows) { | ||
this.rows = Collections.unmodifiableList(new ArrayList<>(rows)); | ||
} | ||
|
||
public static Ladder of(List<Row> rows) { | ||
return new Ladder(rows); | ||
} | ||
|
||
public List<Row> rows() { | ||
return rows; | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package domain; | ||
|
||
public final class LadderHeight { | ||
private final int count; | ||
|
||
private LadderHeight(int count) { | ||
this.count = count; | ||
} | ||
|
||
public static LadderHeight of(int count) { | ||
if (count < 1) { | ||
throw new IllegalArgumentException("행의 개수는 1 이상이어야 합니다"); | ||
} | ||
return new LadderHeight(count); | ||
} | ||
|
||
public int count() { | ||
return count; | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package domain; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
public final class Participants { | ||
private final List<String> names; | ||
|
||
private Participants(List<String> names) { | ||
this.names = Collections.unmodifiableList(new ArrayList<>(names)); | ||
} | ||
|
||
public static Participants of(List<String> rawNames) { | ||
List<String> names = normalizeNames(rawNames); | ||
requireNonEmpty(names); | ||
requireMaxLength(names, 5); | ||
return new Participants(names); | ||
} | ||
|
||
private static List<String> normalizeNames(List<String> rawNames) { | ||
List<String> result = new ArrayList<>(); | ||
for (String n : rawNames) { | ||
String v = n == null ? "" : n.trim(); | ||
if (!v.isEmpty()) { result.add(v); } | ||
} | ||
return result; | ||
} | ||
|
||
private static void requireNonEmpty(List<String> names) { | ||
if (names.isEmpty()) { throw new IllegalArgumentException("이름을 한 개 이상 입력해 주세요"); } | ||
} | ||
|
||
private static void requireMaxLength(List<String> names, int max) { | ||
boolean tooLongExists = names.stream().anyMatch(n -> n.length() > max); | ||
if (tooLongExists) { | ||
throw new IllegalArgumentException("각 이름은 최대 " + max + "글자까지 가능합니다"); | ||
} | ||
} | ||
|
||
public int size() { | ||
return names.size(); | ||
} | ||
|
||
public int indexOf(String name) { | ||
return names.indexOf(name); | ||
} | ||
|
||
public String get(int index) { | ||
return names.get(index); | ||
} | ||
|
||
public List<String> names() { | ||
return names; | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package domain; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
public final class Row { | ||
private final List<Connection> connections; | ||
|
||
private Row(List<Connection> connections) { | ||
this.connections = Collections.unmodifiableList(new ArrayList<>(connections)); | ||
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. 이런게 있었군요! 자세한 내용이 궁금해서 찾아봤어요! 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. 캡슐화를 진행해도 좋을 것 같습니다!! 😀 코드 작성 당시, 저 방식이 성능과 안전성을 모두 고려한 좋은 방법이라고 생각했었습니다. 생성 시 한 번만 방어적 복사를 해두면 잦은 호출에도 성능 부담이 적고, 불변인 enum 요소를 변경 불가 뷰로 제공하니 데이터 안정성도 충분히 확보된다고 보았습니다. |
||
} | ||
|
||
public static Row of(List<Connection> connections) { | ||
validate(connections); | ||
return new Row(connections); | ||
} | ||
|
||
private static void validate(List<Connection> connections) { | ||
Connection previous = Connection.EMPTY; | ||
for (Connection current : connections) { | ||
if (previous == Connection.CONNECTED && current == Connection.CONNECTED) { | ||
throw new IllegalArgumentException("인접한 연결은 허용되지 않습니다"); | ||
} | ||
previous = current; | ||
} | ||
} | ||
|
||
public List<Connection> connections() { | ||
return connections; | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package domain; | ||
|
||
public final class RungLength { | ||
private final int length; | ||
|
||
private RungLength(int length) { | ||
this.length = length; | ||
} | ||
|
||
public static RungLength of(int length) { | ||
if (length < 1) { | ||
throw new IllegalArgumentException("길이는 1 이상이어야 합니다"); | ||
} | ||
return new RungLength(length); | ||
} | ||
|
||
public static RungLength defaultFive() { | ||
return of(5); | ||
} | ||
Comment on lines
+17
to
+19
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 int length() { | ||
return length; | ||
} | ||
|
||
public String dashes() { | ||
return "-".repeat(length); | ||
} | ||
|
||
public String spaces() { | ||
return " ".repeat(length); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package service; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import domain.Columns; | ||
import domain.Connection; | ||
import domain.Ladder; | ||
import domain.Row; | ||
|
||
public final class LadderNavigator { | ||
public int traverse(Ladder ladder, Columns columns, int startColumn) { | ||
int currentColumn = startColumn; | ||
for (Row row : ladder.rows()) { | ||
if (currentColumn < columns.numberOfSegments() | ||
&& row.connections().get(currentColumn) == Connection.CONNECTED) { | ||
currentColumn += 1; | ||
continue; | ||
} | ||
if (currentColumn > 0 | ||
&& row.connections().get(currentColumn - 1) == Connection.CONNECTED) { | ||
currentColumn -= 1; | ||
} | ||
} | ||
return currentColumn; | ||
} | ||
|
||
public List<Integer> traverseAll(Ladder ladder, Columns columns) { | ||
List<Integer> results = new ArrayList<>(); | ||
for (int start = 0; start < columns.count(); start++) { | ||
results.add(traverse(ladder, columns, start)); | ||
} | ||
return results; | ||
} | ||
} | ||
|
||
|
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.
main에 너무 많이 몰려 있어요. 함수나 메서드로 쪼개는게 필요해보여요
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.
아차차... 알려주셔서 감사합니다🙇
바로 수정하도록 하겠습니다!!