-
Notifications
You must be signed in to change notification settings - Fork 94
[ 자동차 경주 - 초간단 애플리케이션 ] 미션 제출 #134
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: qkrcodus
Are you sure you want to change the base?
Changes from all commits
d9f9463
6fe844e
86787ab
1439fb3
03c00c6
e6a63be
ed438b1
86900bb
4996961
6f767b5
9879f8c
4820e8a
cb561b2
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,32 @@ | ||
### 1단계 | ||
- 자동차는 이름을 갖는다. | ||
- 자동차는 0-9 사이 랜덤 값을 구한 후 4이상이면 전진, 3이하면 멈춘다. | ||
- 3항 연산자를 쓰지 않는다. | ||
- else 예약어, switch/case 허용하지 않는다. | ||
- 메소드 길이가 15라인이 넘어가지 않도록 구현한다 | ||
- 메인 메소드를 만들지 않는다. | ||
- 테스트 코드 작성한다. | ||
|
||
### 2단계 | ||
- n대의 자동차가 참여가능하다. | ||
- 주어진 횟수동안 자동차 경주 게임을 완료한 후 누가 우승했는지 알 수 있다. | ||
- 우승자는 한 명 이상일 수 있다. | ||
- 나머진 1단계와 유사하다. | ||
|
||
### 3단계 | ||
- 자동차에 이름 부여가능하고, 전진하는 자동차 출력할 때 자동차 이름을 같이 출력한다. | ||
- 자동차 이름은 , 기준으로 구분하며 이름은 5자 이하만 가능하다. | ||
- 메인 메소드를 추가한다. | ||
|
||
### 4단계 | ||
- 모든 로직에 단위 테스트를 구현한다. | ||
- mvc 패턴으로 리팩터링한다. | ||
|
||
### 개인적 목표 | ||
1. 주석이 없어도 읽기 좋은 코드를 짜보자. | ||
2. 메서드가 한가지 기능만 갖도록 분리하다가 따로 관리하는게 좋을 것 같으면 클래스로 구현하자. | ||
3. 단위 테스트 핵심 원칙을 따르자. | ||
4. 개념에 머무르고 있었던 객체 지향적 개념을 활용하여 코드를 작성해보자. | ||
5. 발생할 수 있는 예외 케이스를 고려하여 방어적인 코드를 작성하고, 이를 테스트 코드로 확인하자. | ||
Comment on lines
+25
to
+30
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. 개인적인 목표도 적어주셨군요👍 |
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ dependencies { | |
testImplementation platform('org.assertj:assertj-bom:3.25.1') | ||
testImplementation('org.junit.jupiter:junit-jupiter') | ||
testImplementation('org.assertj:assertj-core') | ||
developmentOnly('org.springframework.boot:spring-boot-devtools') | ||
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. 잘 기억이 나지 않습니다만 아마 Spring Boot DevTools is not configured. 문구가 떠서 추가한 것 같아요! |
||
} | ||
|
||
test { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package racinggame; | ||
|
||
import racinggame.controller.RacingGameController; | ||
|
||
public class Main { | ||
public static void main(String[] args) { | ||
new RacingGameController().run(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package racinggame.controller; | ||
|
||
import racinggame.model.RacingGame; | ||
import racinggame.model.car.policy.numbergenerator.ZeroToNineRandomGenerator; | ||
import racinggame.model.car.policy.decider.ThresholdBaseMoveDecider; | ||
import racinggame.model.car.policy.evaluator.GreaterThanOrEqualThresholdEvaluator; | ||
import racinggame.model.car.policy.strategy.MoveStrategy; | ||
import racinggame.model.car.policy.strategy.OneStepMoveStrategy; | ||
import racinggame.view.InputView; | ||
import racinggame.view.OutputView; | ||
|
||
import java.util.List; | ||
|
||
public class RacingGameController { | ||
public void run(){ | ||
List<String> names= InputView.readCarNames(); | ||
int moveCount = InputView.readMoveCount(); | ||
|
||
MoveStrategy strategy = new OneStepMoveStrategy( | ||
new ZeroToNineRandomGenerator(), | ||
new ThresholdBaseMoveDecider(new GreaterThanOrEqualThresholdEvaluator(4))); | ||
|
||
RacingGame game = new RacingGame(names,strategy); | ||
|
||
for (int i = 0; i < moveCount; i++) { | ||
game.moveOneTurn(); | ||
OutputView.printResult(game.getCars()); | ||
} | ||
|
||
List<String> winners = game.getWinners(); | ||
OutputView.printWinner(winners); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package racinggame.model; | ||
import racinggame.model.car.Car; | ||
import racinggame.model.car.policy.strategy.MoveStrategy; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class RacingGame { | ||
private final List<Car> cars; | ||
|
||
public RacingGame(List<String> names,MoveStrategy moveStrategy) { | ||
this.cars = createCars(names,moveStrategy); | ||
} | ||
|
||
private List<Car> createCars(List<String> names, MoveStrategy strategy) { | ||
return names.stream() | ||
.map(name -> new Car(name,strategy)) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
public void moveOneTurn() { | ||
for (Car car : cars) { | ||
car.move(); | ||
} | ||
} | ||
|
||
public List<Car> getCars() { | ||
return cars; | ||
} | ||
|
||
public List<String> getWinners(){ | ||
int maxDistance=findMaxDistance(); | ||
|
||
return cars.stream() | ||
.filter(car->car.getMovedDistance()==maxDistance) | ||
.map(Car::getName).collect(Collectors.toList()); | ||
} | ||
|
||
private int findMaxDistance(){ | ||
return cars.stream() | ||
.mapToInt(Car::getMovedDistance) | ||
.max() | ||
.orElse(0); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package racinggame.model; | ||
|
||
import racinggame.model.car.Car; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class WinnerFinder { | ||
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. 실수로 2단계를 건너뛰고 3단계 구현을 ( RacingGame ) 먼저 해뒀기 때문에 해당 클래스는 2단계 구현 내용을 보여주기 위해 별도로 작성한 클래스입니다! 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. 이 클래스를 테스트해보기 위한 WinnerFindTest 클래스도 있는데 둘 다 삭제할까요! |
||
private final List<Car> cars; | ||
private final int moveCount; | ||
public WinnerFinder(List<Car> cars, int moveCount){ | ||
this.cars = cars; | ||
this.moveCount = moveCount; | ||
} | ||
|
||
public void play(){ | ||
for(int i=0;i<moveCount;i++){ | ||
moveAllCars(); | ||
} | ||
} | ||
public List<String> getWinners(){ | ||
int maxDistance=maxDistance(); | ||
return cars.stream().filter(car->car.getMovedDistance()==maxDistance).map(Car::getName).collect(Collectors.toList()); | ||
} | ||
|
||
private void moveAllCars( ){ | ||
for (Car car : cars) { | ||
car.move(); | ||
} | ||
} | ||
private int maxDistance(){ | ||
int max=0; | ||
for (Car car : cars) { | ||
max=Math.max(max,car.getMovedDistance()); | ||
} | ||
return max; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package racinggame.model.car; | ||
|
||
import racinggame.model.car.policy.strategy.MoveStrategy; | ||
|
||
import java.util.Objects; | ||
|
||
public class Car { | ||
private final String name; | ||
private int movedDistance=0; | ||
|
||
private final MoveStrategy moveStrategy; | ||
|
||
public Car(String name,MoveStrategy moveStrategy) { | ||
if(name==null){ | ||
throw new IllegalArgumentException("이름은 null 일 수 없습니다."); | ||
} | ||
if(name.isBlank()){ | ||
throw new IllegalArgumentException("이름은 공백일 수 없습니다."); | ||
} | ||
this.name = name; | ||
this.moveStrategy=moveStrategy; | ||
} | ||
|
||
public void move(){ | ||
int distance=moveStrategy.addStepSize(); | ||
movedDistance+=distance; | ||
} | ||
|
||
public int getMovedDistance() { | ||
return movedDistance; | ||
} | ||
public String getName() { | ||
return name; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package racinggame.model.car.policy.decider; | ||
|
||
public interface MoveDecider { | ||
boolean canMove(int randomNumber); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package racinggame.model.car.policy.decider; | ||
|
||
import racinggame.model.car.policy.evaluator.ThresholdEvaluator; | ||
|
||
public class ThresholdBaseMoveDecider implements MoveDecider { | ||
private final ThresholdEvaluator evaluator; | ||
|
||
public ThresholdBaseMoveDecider(ThresholdEvaluator evaluator) { | ||
this.evaluator = evaluator; | ||
} | ||
|
||
@Override | ||
public boolean canMove(int randomNumber){ | ||
return evaluator.isSatisfied(randomNumber); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package racinggame.model.car.policy.evaluator; | ||
|
||
public class GreaterThanOrEqualThresholdEvaluator implements ThresholdEvaluator { | ||
private final int threshold; | ||
|
||
public GreaterThanOrEqualThresholdEvaluator(int threshold) { | ||
this.threshold = threshold; | ||
} | ||
@Override | ||
public boolean isSatisfied(int value){ | ||
return value >= threshold; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package racinggame.model.car.policy.evaluator; | ||
|
||
public interface ThresholdEvaluator { | ||
boolean isSatisfied(int value); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package racinggame.model.car.policy.numbergenerator; | ||
|
||
public interface RandomGenerator { | ||
int generate(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package racinggame.model.car.policy.numbergenerator; | ||
import java.util.Random; | ||
|
||
public class ZeroToNineRandomGenerator implements RandomGenerator { | ||
private final Random random=new Random(); | ||
|
||
@Override | ||
public int generate() { | ||
return random.nextInt(10); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package racinggame.model.car.policy.strategy; | ||
|
||
public interface MoveStrategy { | ||
int addStepSize(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package racinggame.model.car.policy.strategy; | ||
|
||
import racinggame.model.car.policy.numbergenerator.RandomGenerator; | ||
import racinggame.model.car.policy.decider.MoveDecider; | ||
|
||
public class OneStepMoveStrategy implements MoveStrategy { | ||
private static final int STEP_SIZE = 1; | ||
|
||
private final RandomGenerator randomGenerator; | ||
private final MoveDecider moveDecider; | ||
|
||
public OneStepMoveStrategy(RandomGenerator randomGenerator, MoveDecider moveDecider) { | ||
this.randomGenerator = randomGenerator; | ||
this.moveDecider = moveDecider; | ||
} | ||
@Override | ||
public int addStepSize(){ | ||
int randomNumber=randomGenerator.generate(); | ||
if(moveDecider.canMove(randomNumber)){ | ||
return STEP_SIZE; | ||
} | ||
return 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package racinggame.view; | ||
|
||
import java.util.Arrays; | ||
import java.util.List; | ||
import java.util.Scanner; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
public class InputView { | ||
private static final String DELIMITER = ","; | ||
private static final String CAR_NAME_INPUT_PROMPT ="경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."; | ||
|
||
public static List<String> readCarNames() { | ||
Scanner scanner = new Scanner(System.in); | ||
System.out.println(CAR_NAME_INPUT_PROMPT); | ||
|
||
List<String> names = Arrays.stream(scanner.nextLine().split(DELIMITER)) | ||
.map(String::trim) | ||
.toList(); | ||
|
||
if (names.stream().anyMatch(name -> name.length() > 5)) { | ||
throw new IllegalArgumentException("이름은 5자 이하만 가능합니다."); | ||
} | ||
|
||
Set<String> unique = names.stream().collect(Collectors.toSet()); | ||
if (unique.size() != names.size()) { | ||
throw new IllegalArgumentException("자동차 이름은 모두 고유해야 합니다."); | ||
} | ||
|
||
return names; | ||
} | ||
|
||
public static int readMoveCount() { | ||
Scanner scanner = new Scanner(System.in); | ||
System.out.println("시도할 회수는 몇회인가요?"); | ||
|
||
int moveCount = scanner.nextInt(); | ||
if (moveCount <= 0) { | ||
throw new IllegalArgumentException("moveCount는 0보다 큰 값이여야 합니다."); | ||
} | ||
|
||
return moveCount; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package racinggame.view; | ||
|
||
import racinggame.model.car.Car; | ||
|
||
import java.util.List; | ||
|
||
public class OutputView { | ||
public static void printResult(List<Car> cars) { | ||
System.out.println("\n실행 결과"); | ||
for (Car car : cars) { | ||
System.out.println(car.getName() + " : " + "-".repeat(car.getMovedDistance())); | ||
} | ||
System.out.println(); | ||
} | ||
|
||
public static void printWinner(List<String> winners) { | ||
System.out.println(String.join(", ", winners) + "가 최종 우승했습니다."); | ||
} | ||
|
||
} |
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.
요구사항이 충족되지 않은것으로 보여요!
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.
추가적으로 현재는 자동차 이름이 중복될 수 있는데요. 고유한 자동차 이름을 갖게 변경해보면 어떨까요?
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.
InputView 에서 5자 초과 이름, 중복되는 자동차 입력 시 예외를 던지게 수정하였고, InputViewTest에서 이를 확인했습니다.