-
Notifications
You must be signed in to change notification settings - Fork 122
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단계 - 웹 자동차 경주] 히이로(문제웅) 미션 제출합니다. #53
Changes from all commits
cc2eca5
eabde70
052da95
f35e46c
07c2410
5479166
5cb9800
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 |
---|---|---|
@@ -1 +1,54 @@ | ||
# jwp-racingcar | ||
|
||
# 자동차 경주 구현 | ||
|
||
## 💡프로젝트 개요 | ||
- 자동차 경주 프로젝트는 경주할 자동차 이름과 시도할 횟수를 입력받은 후 전진 과정을 통해 가장 많이 전진한 자동차가 우승을 하는 프로젝트이다. 이 때 자동차 경주 게임을 진행하고 최종 우승 결과를 출력해야한다. | ||
- 진행한 게임 결과는 DB에 저장된다. | ||
--- | ||
|
||
|
||
## 📋 구현 기능 목록 | ||
|
||
## 프로그램 전체 로직 | ||
- ✅자동차 이름에 대한 사용자 입력을 받는다. | ||
- ✅총 시도 횟수에 대한 사용자 입력을 받는다. | ||
- ✅자동차 경주를 진행한다. | ||
- ✅DB에 결과 값들을 저장한다. | ||
- ✅경주 결과를 출력한다. | ||
|
||
|
||
## domain(model) | ||
- ✅자동차 정보를 저장하고 관리한다. | ||
- ✅각 자동차 이름은 1자 이상 5자 이하여야 한다. | ||
- ✅자동차의 위치를 증가 시킬 수 있다. | ||
- ✅자동차의 전진 여부를 판단한다. | ||
- ✅random 값이 4 이상일 경우 전진한다. | ||
- ✅random 값이 3 이하일 경우 정지한다. | ||
- ✅생성된 자동차들의 정보를 저장하고 관리한다. | ||
- ✅자동차 대수는 1대 이상이어야 한다. | ||
- ✅우승한 자동차를 제공한다. | ||
- ✅모든 자동차들이 전진을 1회 시도한다. | ||
- ✅random 하게 0~9 범위 내에 한 자리 숫자를 반환한다. | ||
- ✅자동차 경주를 진행한다. | ||
- ✅몇 라운드를 진행할 지 저장한다. | ||
- ✅라운드가 1 이상의 값으로 주어지는지 검증한다. | ||
- ✅현재 게임진행 현황을 통해 게임 종료 여부를 판단한다. | ||
|
||
|
||
## view | ||
### 입력 | ||
- ✅자동차 이름 입력받기 | ||
- ✅총 시도 횟수 입력받기 | ||
- ✅시도 횟수 입력값은 숫자여야 한다. | ||
- ✅총 시도 횟수는 1 이상의 양의 정수 값이어야 한다. | ||
|
||
### 출력 | ||
- ✅프로그램 실행 결과 | ||
- ✅최종 결과 문구 출력 | ||
- ✅공동 우승자 발생 경우 고려 | ||
|
||
|
||
## 예외 처리 | ||
- ✅자동차 이름이 0자인 경우 고려하기 | ||
- ✅총 시도 횟수 입력값이 양의 정수가 아닌 경우 고려하기 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package racingcar; | ||
|
||
import racingcar.controller.ConsoleRacingCarController; | ||
import racingcar.domain.RandomNumberGenerator; | ||
import racingcar.view.InputView; | ||
import racingcar.view.OutputView; | ||
|
||
import java.util.Scanner; | ||
|
||
public class ConsoleRacingCarApplication { | ||
|
||
public static void main(String[] args) { | ||
ConsoleRacingCarController controller = new ConsoleRacingCarController(new InputView(new Scanner(System.in)), | ||
new OutputView(), | ||
new RandomNumberGenerator()); | ||
|
||
controller.run(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package racingcar.controller; | ||
|
||
import racingcar.domain.Car; | ||
import racingcar.domain.Cars; | ||
import racingcar.domain.NumberGenerator; | ||
import racingcar.domain.RacingGame; | ||
import racingcar.view.InputView; | ||
import racingcar.view.OutputView; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class ConsoleRacingCarController { | ||
|
||
private final InputView inputView; | ||
private final OutputView outputView; | ||
private final NumberGenerator numberGenerator; | ||
|
||
public ConsoleRacingCarController(InputView inputView, OutputView outputView, NumberGenerator numberGenerator) { | ||
this.inputView = inputView; | ||
this.outputView = outputView; | ||
this.numberGenerator = numberGenerator; | ||
} | ||
|
||
public void run() { | ||
Cars cars = generateCars(inputView.readCarNames()); | ||
int round = inputView.readRacingRound(); | ||
playGame(cars, round); | ||
} | ||
|
||
private Cars generateCars(List<String> carNames) { | ||
List<Car> carInstances = new ArrayList<>(); | ||
for (String name : carNames) { | ||
carInstances.add(new Car(name, numberGenerator)); | ||
} | ||
return new Cars(carInstances); | ||
} | ||
|
||
private void playGame(Cars cars, int round) { | ||
RacingGame racingGame = new RacingGame(cars, round); | ||
outputView.printResultMessage(); | ||
while (!racingGame.isGameEnded()) { | ||
outputView.printRoundResult(racingGame.playOneRound()); | ||
} | ||
outputView.printFinalResult(racingGame.findWinnerCars()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package racingcar.controller; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import racingcar.dto.request.RacingStartRequest; | ||
import racingcar.dto.response.RacingResultResponse; | ||
import racingcar.service.RacingCarService; | ||
|
||
@RestController | ||
public class WebRacingCarController { | ||
|
||
private final RacingCarService racingCarService; | ||
|
||
public WebRacingCarController(RacingCarService racingCarService) { | ||
this.racingCarService = racingCarService; | ||
} | ||
|
||
@PostMapping("/plays") | ||
public ResponseEntity<RacingResultResponse> play(@RequestBody RacingStartRequest racingStartRequest) { | ||
return ResponseEntity.ok(racingCarService.play(racingStartRequest)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package racingcar.dao; | ||
|
||
import racingcar.dto.CarDto; | ||
|
||
import java.util.List; | ||
|
||
public interface CarDao { | ||
|
||
void save(int gameId, List<CarDto> carDtos); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package racingcar.dao; | ||
|
||
import org.springframework.stereotype.Repository; | ||
import racingcar.dto.CarDto; | ||
import racingcar.utils.ConnectionProvider; | ||
|
||
import java.sql.Connection; | ||
import java.sql.PreparedStatement; | ||
import java.sql.SQLException; | ||
import java.util.List; | ||
|
||
@Repository | ||
public class H2CarDao implements CarDao { | ||
|
||
@Override | ||
public void save(int gameId, List<CarDto> carDtos) { | ||
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.
저는 View에 도메인을 넘겨도 되는가와 이 질문은 동일하다고 생각해요.
저도 동의하는 바입니다. 히이로의 경험을 쌓아가다 보면 좀 더 명확한 근거를 들 수 있을 거에요. 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.
위에서 Repository와 Dao에 대해서 리뷰를 받고 정리하기는 했는데 해당 부분에 대한 리뷰였을까요? 혹시 이 부분이 아니었다면 어떤 부분에 대한 리뷰였는지 좀 더 자세한 설명을 부탁드리고 싶습니다! 🙇 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단계에서 해당 부분들이 잘 해결된 것 같네요 👍 |
||
String sql = "INSERT INTO CAR(game_id, name, position, is_win) VALUES (?,?,?,?)"; | ||
|
||
try (Connection connection = ConnectionProvider.getConnection()) { | ||
for (CarDto carDto : carDtos) { | ||
PreparedStatement ps = connection.prepareStatement(sql); | ||
|
||
ps.setInt(1, gameId); | ||
ps.setString(2, carDto.getName()); | ||
ps.setInt(3, carDto.getPosition()); | ||
ps.setBoolean(4, carDto.isWin()); | ||
ps.executeUpdate(); | ||
|
||
ps.close(); | ||
} | ||
} catch (SQLException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package racingcar.dao; | ||
|
||
import org.springframework.stereotype.Repository; | ||
import racingcar.utils.ConnectionProvider; | ||
|
||
import java.sql.Connection; | ||
import java.sql.PreparedStatement; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.sql.Statement; | ||
|
||
@Repository | ||
public class H2RacingGameDao implements RacingGameDao { | ||
|
||
@Override | ||
public int save(int count) { | ||
String sql = "INSERT INTO RACING_GAME(count) VALUES (?)"; | ||
|
||
try (Connection connection = ConnectionProvider.getConnection(); | ||
Comment on lines
+12
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. 스프링의 기능를 활용해서 구현해보시면 좋을 것 같아요. 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. 넵 이번에는 학습테스트를 먼저 진행하지 않고 바로 1단계 미션을 먼저 진행했었어서 JdbcTemplate 기능을 활용하지 못했네요... 2단계 때 수정해보도록 하겠습니다! |
||
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { | ||
ps.setInt(1, count); | ||
ps.executeUpdate(); | ||
|
||
ResultSet rs = ps.getGeneratedKeys(); | ||
|
||
if (rs.next()) { | ||
return rs.getInt(1); | ||
} | ||
|
||
throw new IllegalStateException(); | ||
|
||
} catch (SQLException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package racingcar.dao; | ||
|
||
public interface RacingGameDao { | ||
|
||
int save(int count); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package racingcar.domain; | ||
|
||
import java.util.Objects; | ||
|
||
public class Car implements Comparable<Car> { | ||
|
||
private static final int NAME_MIN_LENGTH = 1; | ||
private static final int NAME_MAX_LENGTH = 5; | ||
private static final int MOVABLE_MIN_NUMBER = 4; | ||
private static final String NAME_LENGTH_ERROR = "[ERROR] 자동차 이름은 1자 이상 5자 이하여야 합니다."; | ||
|
||
private String name; | ||
private int position; | ||
private NumberGenerator numberGenerator; | ||
|
||
public Car(String name, NumberGenerator numberGenerator) { | ||
name = name.trim(); | ||
validateName(name); | ||
this.name = name; | ||
this.position = 0; | ||
this.numberGenerator = numberGenerator; | ||
} | ||
|
||
public void goForward() { | ||
if (numberGenerator.generate() >= MOVABLE_MIN_NUMBER) { | ||
position++; | ||
} | ||
} | ||
|
||
public boolean isSamePosition(Car otherCar) { | ||
return this.position == otherCar.position; | ||
} | ||
|
||
public int getPosition() { | ||
return this.position; | ||
} | ||
|
||
public String getName() { | ||
return this.name; | ||
} | ||
|
||
@Override | ||
public int compareTo(Car otherCar) { | ||
return otherCar.position - this.position; | ||
} | ||
|
||
private void validateName(String name) { | ||
if (name.length() < NAME_MIN_LENGTH || name.length() > NAME_MAX_LENGTH) { | ||
throw new IllegalArgumentException(NAME_LENGTH_ERROR); | ||
} | ||
} | ||
|
||
@Override | ||
public boolean equals(Object other) { | ||
if (this == other) return true; | ||
if (other == null || getClass() != other.getClass()) return false; | ||
Car car = (Car) other; | ||
return position == car.position && Objects.equals(name, car.name); | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return Objects.hash(name, position); | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package racingcar.domain; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
public class Cars { | ||
|
||
private static final int CARS_MIN_SIZE = 1; | ||
private static final String CARS_SIZE_ERROR = "[ERROR] 자동차 대수는 1이상이어야 합니다."; | ||
|
||
private List<Car> cars; | ||
|
||
public Cars(List<Car> cars) { | ||
validateCarsSize(cars); | ||
this.cars = new ArrayList<>(cars); | ||
} | ||
|
||
public List<Car> findAllWinner() { | ||
Car maxPositionCar = findMaxPositionCar(); | ||
return cars.stream() | ||
.filter(car -> car.isSamePosition(maxPositionCar)) | ||
.collect(Collectors.toList()); | ||
} | ||
|
||
public List<Car> moveEachCar() { | ||
for (Car car : cars) { | ||
car.goForward(); | ||
} | ||
return new ArrayList<>(cars); | ||
} | ||
|
||
// Todo : 나중에 중복되는 이름은 입력되면 예외처리 되도록 구현해야 한다. | ||
private void validateCarsSize(List<Car> cars) { | ||
if (cars.size() < CARS_MIN_SIZE) { | ||
throw new IllegalArgumentException(CARS_SIZE_ERROR); | ||
} | ||
} | ||
|
||
private Car findMaxPositionCar() { | ||
cars.sort(Car::compareTo); | ||
return cars.get(0); | ||
} | ||
|
||
public List<Car> getCars() { | ||
return cars; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package racingcar.domain; | ||
|
||
@FunctionalInterface | ||
public interface NumberGenerator { | ||
|
||
int generate(); | ||
} |
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.
레퍼런스 감사합니다 범블비! 덕분에 아주 약간은 감을 잡은 것 같기도 하네요 ㅎㅎ... 일단 이번 미션의 주된 내용은 아니니 우선순위를 조금 낮춰서 가져가 보겠습니다!