-
Notifications
You must be signed in to change notification settings - Fork 907
[자동차 경주] 김도현 미션 제출합니다. #357
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: main
Are you sure you want to change the base?
Conversation
기능 목록을 입력, 예외, 메인 로직, 출력 네 가지로 나누어 추가함
Car 객체 생성 시, 객체의 name을 Validator.java에서 검증한다 - 입력값이 null인 경우 - 입력값이 빈 문자열이거나 공백으로 이루어진 경우 - 입력값이 5자를 초과하는 경우 위의 세 가지 경우를 검증하여 만약 위 경우에 해당하면 IllegalArgumentException을 반환한다.
- findMaxPosition으로 Car 리스트에서 가장 큰 position을 탐색 - findWinners로 maxPosition의 값을 가지는 Car 객체의 name을 winners에 저장
- pickNumberInRange에서 0부터 9까지 중 정수 반환 - Car 목록을 순회하며 난수를 생성하고 move에 난수를 넣어 position 변경 README의 "0부터 9 사이 무작위 정수를 구하기"는 제공된 라이브러리를 사용하므로 제거
try-catch문으로 parseInt하여 입력값이 정수가 아닌 경우 IllegalArgumentException를 던짐
paseInt를 시도하는 것은 동일하므로 실제 기능에서는 입력값이 숫자가 아니거나, 정수가 아닌 경우 모두 예외 처리함
- CarFactory에서 쉼표를 구분자로 하여 입력받은 문자열에서 이름을 분리함 - 분리된 이름을 갖는 Car 객체의 List 생성 후 반환
RacingGame: RacingGame.getCarList를 통해 Car 목록 획득 InputView: 검증 책임을 CarFactory에서 Car 목록을 생성할 때, Car 생성자에서 처리 OutputView: 횟수별 상태와 최종 우승자 출력 Application: 메인 로직 제어 기능 추가
Before: - main에서 입력받은 횟수만큼 runOneRound() 메서드가 실행되는데, 이때 난수를 가져와 Car 객체의 move 메서드를 실행한다. - move함수가 MIN_INCREASE_CONDITION(과제에서 4이상) 이상인 경우에만 INCREASE_POSITION(과제에서 1)만큼 position을 더한다. - 문제점 1. move()라는 메서드명에 맞지 않는다. 조건에 따라 움직이기 때문에 “움직인다”라는 이름의 메서드에서 기대하는 행동과는 조금 다를 수 있다. 2. runOneRound()에서 난수에 대해 Car 객체가 어떻게 움직이는지 알 수 없고 Car 클래스의 메서드를 확인해야 한다. After: - Car 클래스의 move() 메서드는 이제 position 필드 값을 INCREASE_POSITION만큼 더해 저장하는 기능만 수행한다. - RacingGame 클래스의 runOneRound() 메서드는 난수를 생성 후 조건에 부합하면 car.move()를 호출하는 방식으로, 어떻게 하나의 라운드가 흘러가는지 정확하게 이해할 수 있도록 했다. - 변경사항에 맞도록 Test를 수정했다.
기존 main에서 자동차 경주의 준비부터 실행, 결과 출력 로직을 모두 수행하던 책임을 RacingGameController로 전달함 - 의존성 주입을 사용하여 main에서 객체 생성 후 주입받아 사용함 - 의존성 주입을 위해 CarFactory, InputView, OutputView의 메서드를 정적 메서드에서 일반 메서드로 변경
CarFactory 클래스에서 createCarList()메서드 호출 시 name 리스트와 set 간의 길이 차이가 발생하는 경우 이름에 중복이 있으므로 예외 처리 - 이름 중복 입력 시 예외 발생하는 Test추가
dohyunk58
left a comment
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.
다른 분들의 코드를 보고 스스로 점검해봤습니다.
| private List<Car> setupCars() { | ||
| String carNameInput = inputView.readCarNames(); | ||
| return carFactory.createCarList(carNameInput); | ||
| } |
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.
변수 이름에 타입을 적지 않아야 합니다. nameList -> names 등
| public List<Car> getCarList() { | ||
| return carList; | ||
| } |
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.
getter로 참조 가능한 객체를 반환하는 경우 수정 가능성이 있으므로 깊은 복사를 통해 수정을 방지해야 합니다.
sun007021
left a comment
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.
고생 많으셨습니다!
저도 잘 몰라서 제가 남긴 질문이나 의견이 맞지 않을 수 있으니 참고만 해주세요!
| public static void validateDuplicateNames(List<String> names) { | ||
| Set<String> uniqueNames = new HashSet<>(names); | ||
|
|
||
| if (uniqueNames.size() != names.size()) { |
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.
이름이 중복되는 경우에 대해서는 생각을 안해봤는데 이런 예외가 있을 수도 있겠군요!
|
|
||
| public class InputView { | ||
| public String readCarNames() { | ||
| System.out.println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)"); |
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.
맞습니다. @sun007021 님의 PR에서 확인한 점으로 입력값 자체가 null이거나 공백인 경우 예외처리하는 기능이 필요합니다.
|
|
||
| public void run() { | ||
| // 1. 준비 | ||
| List<Car> cars = setupCars(); |
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를 바로 사용하지 않고 이렇게 함수로 한번 더 포장해서 사용했을때의 장점이 궁금합니다
| return Integer.parseInt(tryCountInput); | ||
| } | ||
|
|
||
| private void playRacingGame(RacingGame game, int tryCount) { |
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.
Controller에 전체 게임 라운드를 반복하는 로직을 직접 구현하는 것보다 RacingGame에서 동작하는 것이 역할이 더 잘 분리될거 같다고 생각하는데 어떻게 생각하시나요?
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.
맞습니다. 저도 Controller가 반복을 수행하지 않는 것이 이상적이라고 생각합니다.
그래서 대안을 생각해보던 중 현재 코드 설계에서는 Controller에서 View를 호출하기 때문에 만약 루프가 RacingGame 클래스로 이동하면 반복마다 출력하는 경우에 View를 호출하기가 까다로워지는 문제를 발견했습니다.
구조를 수정한다고 생각해보면 게임 결과를 저장하고 출력하면 해결할 수 있을 것 같습니다. 피드백 감사합니다!
2주차 자동차 경주 미션 구현
작업 내용
자동차 경주 게임을 TDD, MVC, DI 패턴을 기반으로 구현했습니다.
구현 기능 목록
핵심 설계 및 고민
요구 사항을 충족하기 위해 객체 지향 설계 원칙을 적용하려 노력했습니다.
1. MVC 패턴 적용
Controller
Model
View
2. TDD 기반 구현
Model 계층의 모든 비즈니스 로직과 예외 상황에 대해 JUnit 5와 AssertJ를 사용하여 단위 테스트를 우선 작성하고 이를 통과하는 코드를 구현했습니다.
3. 의존성 주입(DI)
Application(main)이 InputView, OutputView, CarFactory 등 필요한 객체를 생성하여 RacingGameController의 생성자로 주입합니다. Controller가 View의 구체적인 구현이 아닌 인스턴스에 의존하게 되어 View와의 결합도가 낮아지며 Controller의 테스트가 용이해졌습니다.
4. 단일 책임 원칙(SRP)
각 객체가 하나의 책임만 갖도록 분리했습니다. CarFactory는 '생성', Validator는 '검증', RacingGame은 '게임 로직', Controller는 '흐름 제어'의 책임을 각각 담당합니다.