Skip to content
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

[LBP] 현정빈 자동차 경주 미션 1,2,3 단계 제출합니다. #88

Merged
merged 10 commits into from
Feb 16, 2025
9 changes: 9 additions & 0 deletions src/main/java/RacingGameApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import controller.RacingGameController;

public class RacingGameApplication {

public static void main(String[] args) {
RacingGameController racingGameController = new RacingGameController();
racingGameController.gamePlay();
}
}
47 changes: 47 additions & 0 deletions src/main/java/controller/RacingGameController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package controller;

import domain.RacingGameService;
import view.InputView;
import view.ResultView;

import java.util.List;

public class RacingGameController {

private final static int NAME_LENGTH = 5;
private final RacingGameService racingGame = new RacingGameService();
private final InputView inputView = new InputView();
private final ResultView resultView = new ResultView(racingGame);

public void gamePlay() {
List<String> carNames = inputView.inputCarNames();
validateNameLength(carNames);
racingGame.carList(carNames);

int rounds = inputView.inputRaceRounds();
rounds = ensurePositiveRounds(rounds);

System.out.println("실행결과");
racingGame.gameStart(rounds);
resultView.printRaceResults();
}

public void validateNameLength(List<String> carsName) {
for (String carName : carsName) {
validateInput(carName);
}
}

public void validateInput(String carName) {
if (carName.length() > NAME_LENGTH) {
throw new IllegalArgumentException("자동차 이름은 5자 이하여야합니다.");
}
}

public int ensurePositiveRounds(int rounds) {
if (rounds <= 0) {
throw new IllegalArgumentException("시도할 회수는 1이상이어야 합니다.");
}
return rounds;
}
}
32 changes: 32 additions & 0 deletions src/main/java/domain/Car.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package domain;

import java.util.Random;

public class Car {

private static final int MOVE_TRIGGER = 4;
private final String name;
private int position;
private Random randomValue = new Random();

public Car(String name) {
this.name = name;
this.position = 0;
}

public void move() {
int random = randomValue.nextInt(10);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 Random 객체 이름이 randomValue인데, 오히려 생성된 난수를 의미하는 변수가 randomValue가 되는게 더 명확할 거 같다고 생각하는데 어떠세요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 현재 randomValue라는 변수명이 난수를 생성하는 객체를 의미하고 있지만, 실제로 난수를 저장하는 변수로 사용하면 더 직관적일 것 같습니다.
그리고 랜덤 객체는 randomGenerator로 수정하여 '난수를 생성하는 객체'라고 표현하는 것이 좋을 것 같습니다! 좋은 피드백 감사합니다!

if (random >= MOVE_TRIGGER) {
position++;
}
}

public String getName() {
return name;
}

public int getPosition() {
return position;
}

}
57 changes: 57 additions & 0 deletions src/main/java/domain/RacingGameService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package domain;

import java.util.*;
import java.util.stream.Collectors;

public class RacingGameService {

private List<Car> cars = new ArrayList<>();
private List<Car> winnerCars = new ArrayList<>();
private int rounds;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rounds를 필드로 선언하신 이유가 있을까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

처음에는 rounds를 여러 메서드에서 사용할 것이라고 예상했지만,
실제로는 gameStart() 내부에서만 사용되므로 필드가 아닌 지역 변수로 사용하는 것이 더 적절한 것 같습니다.
불필요한 필드를 줄이면 코드의 명확성이 높아지고 유지보수가 쉬워지므로, rounds 필드는 제거하는 것이 바람직한 것 같습니다.

소중한 시간을 내어 도와주셔서 진심으로 감사드립니다!


public void carList(List<String> carNames) {
cars = carNames.stream()
.map(Car::new)
.collect(Collectors.toList());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 carList() 메서드는 List<String>을 받아 cars 리스트를 새롭게 할당하는 방식이네요! 이 경우 리스트의 불변성이 유지될까요, 아니면 가변적일까요?

참고하면 좋을 자료 공유드립니다!
일급컬렉션

Copy link
Author

@JeongBeanHyun JeongBeanHyun Feb 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 carList() 메서드는 List을 받아 cars 리스트를 새롭게 할당하는 방식입니다.
이 경우 리스트의 불변성이 유지되지 않고 가변적인 것 같습니다...! collect(Collectors.toList())는 기본적으로 ArrayList를 반환하기 때문에 리스트가 변경될 수 있습니다.
리스트를 불변으로 만들려면

  1. Collections.unmodifiableList() 사용
  2. List.copyOf() 사용
  3. 일급 컬렉션 활용

의 방법이 있습니다. 특히 일급 컬렉션 활용 방법은 단순히 리스트를 불변으로 만드는 것뿐만 아니라, 컬렉션의 검증, 검색 등의 기능을 하나의 클래스에서 처리할 수 있어 코드의 유지보수성과 확장성을 높이는 장점이 있습니다.
그동안 List 같은 컬렉션을 직접 다루는 것이 자연스럽다고 생각했는데, 별도의 클래스로 감싸면 관리가 훨씬 편리하고, 코드의 책임이 분리된다는 점이 인상적이었습니다! 좋은 피드백 감사합니다!!

}

public void gameStart(int rounds) {
this.rounds = rounds;
while (rounds-- > 0) {
moveCars();
printRoundResults();
}
}

public void moveCars() {
for (Car car : cars) {
car.move();
}
}

private void printRoundResults() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

printRoundResults()는 라운드 결과를 출력하는 메서드인데, 현재 ResultView가 있음에도 Service에서 처리하신 이유가 있을까요?
혹시 ResultView로 분리하는 것에 대해 어떻게 생각하시나요?

Copy link
Author

@JeongBeanHyun JeongBeanHyun Feb 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

라운드 결과 출력이 단순 출력보다는 게임의 핵심이라는 생각에 Service에 작성하였습니다. 그러나 MVC 패턴으로 생각했을 때, printRoundResults()는 ResultView에서 처리하는 것이 적절한 방법인 것 같습니다.
저도 리팩토링 과정에서 printRoundResults()를 어디에 작성할지 고민이 있었는데, 지윤 리뷰어님 피드백이 올바른 것 같습니다. 좋은 피드백 감사합니다!

for (Car car : cars) {
System.out.println(car.getName() + " : " + "-".repeat(car.getPosition()));
}
System.out.println();
}

public List<Car> getCars() {
return cars;
}

public List<Car> getWinner() {
int maxPosition = cars.stream()
.mapToInt(Car::getPosition)
.max()
.orElse(0);
winnerCars = cars.stream()
.filter(car -> car.getPosition() == maxPosition)
.toList();;
return winnerCars;
}

public String findWinnerName() {
return winnerCars.stream().map(car -> car.getName()).collect(Collectors.joining(", "));
}
}
37 changes: 37 additions & 0 deletions src/main/java/view/InputView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package view;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class InputView {

private final Scanner scanner = new Scanner(System.in);

public List<String> inputCarNames() {
System.out.println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).");
String inputValue = scanner.nextLine();
String[] carNamesArray = inputValue.split(",");
List<String> carNames = new ArrayList<>();
for (String carName : carNamesArray) {
carNames.add(carName.trim());
}
return carNames;
}

public int inputRaceRounds() {
System.out.println("시도할 회수는 몇회인가요?");
int rounds = promptForValidNumber();
System.out.println();
return rounds;
}

public int promptForValidNumber() {
while (!scanner.hasNextInt()) {
System.out.println("잘못된 입력입니다. 숫자를 다시 입력해주세요.");
scanner.next();
}
return scanner.nextInt();
}

}
20 changes: 20 additions & 0 deletions src/main/java/view/ResultView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package view;

import domain.RacingGameService;

public class ResultView {

private final RacingGameService racingGame;

public ResultView(RacingGameService racingGame) {
this.racingGame = racingGame;
}

public void printRaceResults() {
racingGame.getWinner();

System.out.print(racingGame.findWinnerName());
System.out.println("가 최종 우승했습니다.");
}

}