-
Notifications
You must be signed in to change notification settings - Fork 452
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
[1단계 - 자동차 경주 구현] 프린(남기범) 미션 제출합니다. #668
Changes from all commits
eaa23a9
788c709
00c9f90
cc21e68
054d28e
104bb98
570a5bc
68683e1
05bc950
8db9152
40b2e67
4180da2
b702ab8
ccd00ac
9b95a13
f2316ec
ea6e6c3
22259fc
ce4056d
92e0150
917e0b0
9fdd000
2e92cf2
2fb111d
6ffd25a
1cb4aa1
b57f517
30d6ae6
d433dcf
4863d7c
2eda3b7
e6a0a93
4aeab79
50e8c2f
14bcb70
9905671
588c6b6
2e585d3
514a45f
fd044fc
3f6a64c
299e9b5
3d1c595
2ab8cdb
85f4d34
cc143b7
5da3592
0d58862
3eb4d45
4c4845b
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,17 @@ | ||
import application.RaceService; | ||
import controller.RaceController; | ||
import ui.InputView; | ||
import ui.OutputView; | ||
import util.CarNamesValidator; | ||
import util.RandomNumberGenerator; | ||
import util.TryCountValidator; | ||
|
||
public class Main { | ||
public static void main(String[] args) { | ||
final InputView inputView = new InputView(new CarNamesValidator(), new TryCountValidator()); | ||
final OutputView outputView = new OutputView(); | ||
final RaceService raceService = new RaceService(new RandomNumberGenerator()); | ||
final RaceController raceController = new RaceController(inputView, outputView, raceService); | ||
raceController.start(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package application; | ||
|
||
import java.util.List; | ||
import model.Car; | ||
import model.Cars; | ||
import util.NumberGenerator; | ||
|
||
public class RaceService { | ||
private static final int MOVE_THRESHOLD = 4; | ||
private final NumberGenerator numberGenerator; | ||
|
||
public RaceService(NumberGenerator numberGenerator) { | ||
this.numberGenerator = numberGenerator; | ||
} | ||
|
||
public void moveCars(Cars cars) { | ||
cars.getCars() | ||
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. 8칸 indent를 쓰시는군요?! 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. 아하 ㅎㅎ 우테코 코드 스타일이었군요 |
||
.forEach(car -> { | ||
int number = numberGenerator.generateNumber(); | ||
if (number >= MOVE_THRESHOLD) { | ||
car.moveForward(); | ||
} | ||
}); | ||
} | ||
|
||
public List<String> findWinners(Cars cars) { | ||
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. cars.findMaxPosition 기능은 일급컬렉션인 cars 가 맡도록 하셨군요 :) 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. cars가 맡고 있다가 우승자는 서비스에서 판단하는게 맞지 않나? 싶어서 다시 바꾸게 되었는데 cars가 하는게 더 좋을 것 같네요 행위를 한 곳에서 관리할 수 있는 일급 컬렉션의 장점도 살릴 수 있구요! 감사합니다😁 |
||
int maxPosition = cars.findMaxPosition(); | ||
return cars.getCars().stream() | ||
.filter(car -> car.isSamePosition(maxPosition)) | ||
.map(Car::getName) | ||
.toList(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package controller; | ||
|
||
import static java.util.stream.Collectors.collectingAndThen; | ||
import static java.util.stream.Collectors.toList; | ||
|
||
import application.RaceService; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
import model.Car; | ||
import model.Cars; | ||
import model.Name; | ||
import model.TryCount; | ||
import ui.InputView; | ||
import ui.OutputView; | ||
|
||
public class RaceController { | ||
private final InputView inputView; | ||
private final OutputView outputView; | ||
private final RaceService raceService; | ||
|
||
public RaceController(InputView inputView, OutputView outputView, RaceService raceService) { | ||
this.inputView = inputView; | ||
this.outputView = outputView; | ||
this.raceService = raceService; | ||
} | ||
|
||
public void start() { | ||
Cars cars = createCars(); | ||
TryCount tryCount = createTryCount(); | ||
outputView.printResultHeader(); | ||
while (tryCount.hasTryCount()) { | ||
raceService.moveCars(cars); | ||
outputView.printCarNameAndPosition(cars); | ||
tryCount.decreaseTryCount(); | ||
} | ||
List<String> winners = raceService.findWinners(cars); | ||
outputView.printWinners(winners); | ||
} | ||
|
||
private Cars createCars() { | ||
String[] carNames = inputView.inputCarNames(); | ||
return Arrays.stream(carNames) | ||
.map(Name::new) | ||
.map(Car::new) | ||
.collect(collectingAndThen(toList(), Cars::new)); | ||
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 TryCount createTryCount() { | ||
int tryCount = inputView.inputTryCount(); | ||
return new TryCount(tryCount); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package enums; | ||
|
||
public enum Delimiter { | ||
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을 유용한 방식으로 사용하셨네요 👍 |
||
COMMA(",", "^[a-zA-Z가-힣\\d]+(,[a-zA-Z가-힣\\d]+)*$", "쉼표"); | ||
|
||
private final String value; | ||
private final String regex; | ||
private final String korName; | ||
|
||
Delimiter(String value, String regex, String korName) { | ||
this.value = value; | ||
this.regex = regex; | ||
this.korName = korName; | ||
} | ||
|
||
public String getValue() { | ||
return this.value; | ||
} | ||
|
||
public String getRegex() { | ||
return this.regex; | ||
} | ||
|
||
public String getKorName() { | ||
return this.korName; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package model; | ||
|
||
public class Car { | ||
private final Name name; | ||
private int position; | ||
|
||
public Car(Name name) { | ||
this.name = name; | ||
} | ||
|
||
public void moveForward() { | ||
this.position++; | ||
} | ||
|
||
public boolean isSamePosition(int position) { | ||
return this.position == position; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
if (obj instanceof Car car) { | ||
return this.name.equals(car.name); | ||
} | ||
return false; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return this.name.hashCode(); | ||
} | ||
|
||
public String getName() { | ||
return name.getName(); | ||
} | ||
|
||
public int getPosition() { | ||
return this.position; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package model; | ||
|
||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
public class Cars { | ||
private final List<Car> cars; | ||
|
||
public Cars(List<Car> cars) { | ||
verifyDuplicateCarNames(cars); | ||
this.cars = cars; | ||
} | ||
|
||
private void verifyDuplicateCarNames(List<Car> cars) { | ||
long distinctCount = cars.stream() | ||
.map(Car::getName) | ||
.distinct() | ||
.count(); | ||
|
||
if (distinctCount != cars.size()) { | ||
throw new IllegalArgumentException("중복된 이름의 자동차가 존재합니다."); | ||
} | ||
} | ||
|
||
public int findMaxPosition() { | ||
return cars.stream() | ||
.mapToInt(Car::getPosition) | ||
.max() | ||
.orElse(0); | ||
} | ||
|
||
public List<Car> getCars() { | ||
return Collections.unmodifiableList(cars); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package model; | ||
|
||
public class Name { | ||
private static final int MIN_NAME_LENGTH = 1; | ||
private static final int MAX_NAME_LENGTH = 5; | ||
private final String name; | ||
|
||
public Name(String name) { | ||
verifyNameLength(name); | ||
this.name = name; | ||
} | ||
|
||
private void verifyNameLength(String name) { | ||
if (name.length() < MIN_NAME_LENGTH || name.length() > MAX_NAME_LENGTH) { | ||
throw new IllegalArgumentException( | ||
String.format("이름은 %d자 이상 %d자 이하여야 합니다.", MIN_NAME_LENGTH, MAX_NAME_LENGTH)); | ||
} | ||
} | ||
|
||
protected String getName() { | ||
return name; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
if (this == obj) { | ||
return true; | ||
} | ||
if (obj instanceof Name name) { | ||
return this.name.equals(name.name); | ||
} | ||
return false; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return this.name.hashCode(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package model; | ||
|
||
public class TryCount { | ||
private static final int MIN_TRY_COUNT = 1; | ||
private static final int MAX_TRY_COUNT = 120; | ||
private int tryCount; | ||
|
||
public TryCount(int tryCount) { | ||
verifyTryCount(tryCount); | ||
this.tryCount = tryCount; | ||
} | ||
|
||
private void verifyTryCount(int tryCount) { | ||
if (tryCount < MIN_TRY_COUNT || tryCount > MAX_TRY_COUNT) { | ||
throw new IllegalArgumentException( | ||
String.format("시도 횟수는 %d 이상 %d 이하여야 합니다.", MIN_TRY_COUNT, MAX_TRY_COUNT)); | ||
} | ||
} | ||
|
||
public void decreaseTryCount() { | ||
if (tryCount == 0) { | ||
throw new IllegalStateException("시도 횟수가 모두 소진되었습니다."); | ||
} | ||
tryCount--; | ||
} | ||
|
||
public boolean hasTryCount() { | ||
return tryCount > 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package ui; | ||
|
||
import static enums.Delimiter.COMMA; | ||
|
||
import java.util.Scanner; | ||
import util.InputValidator; | ||
|
||
public class InputView { | ||
private final Scanner scanner = new Scanner(System.in); | ||
private final InputValidator carNamesValidator; | ||
private final InputValidator tryCountValidator; | ||
|
||
public InputView(InputValidator carNamesValidator, InputValidator tryCountValidator) { | ||
this.carNamesValidator = carNamesValidator; | ||
this.tryCountValidator = tryCountValidator; | ||
} | ||
|
||
public String[] inputCarNames() { | ||
System.out.printf("경주할 자동차 이름을 입력하세요(이름은 %s(%s) 기준으로 구분).%n", COMMA.getKorName(), COMMA.getValue()); | ||
String carNames = scanner.nextLine(); | ||
carNamesValidator.validate(carNames); | ||
return carNames.split(COMMA.getValue()); | ||
} | ||
|
||
public int inputTryCount() { | ||
System.out.println("시도할 회수는 몇 회인가요?"); | ||
String tryCount = scanner.nextLine(); | ||
tryCountValidator.validate(tryCount); | ||
return Integer.parseInt(tryCount); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package ui; | ||
|
||
import static enums.Delimiter.COMMA; | ||
|
||
import java.util.List; | ||
import model.Cars; | ||
|
||
public class OutputView { | ||
public void printResultHeader() { | ||
System.out.println(); | ||
System.out.println("실행 결과"); | ||
} | ||
|
||
public void printCarNameAndPosition(Cars cars) { | ||
cars.getCars() | ||
.forEach(car -> | ||
System.out.printf("%s : %s%n", car.getName(), "-".repeat(car.getPosition())) | ||
); | ||
System.out.println(); | ||
} | ||
|
||
public void printWinners(List<String> winners) { | ||
System.out.println(String.join(COMMA.getValue() + " ", winners) + "가 최종 우승했습니다."); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package util; | ||
|
||
import static enums.Delimiter.COMMA; | ||
|
||
import java.util.regex.Pattern; | ||
|
||
public class CarNamesValidator extends InputValidator { | ||
private static final Pattern CAR_NAMES_PATTERN = Pattern.compile(COMMA.getRegex()); | ||
|
||
@Override | ||
void validateCustom(String input) { | ||
if (!CAR_NAMES_PATTERN.matcher(input).matches()) { | ||
throw new IllegalArgumentException( | ||
String.format("자동차 이름은 %s(%s)를 기준으로 구분해야 합니다.", COMMA.getKorName(), COMMA.getValue())); | ||
} | ||
} | ||
} |
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.
👍
기능별로 깔끔하게 잘 정리되었네요