Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
7dfc3e3
docs(README.md): 기능 요구사항 작성
Redish03 Oct 22, 2025
201dd79
docs(README.md): 구현할 기능 작성
Redish03 Oct 22, 2025
0433c6a
docs(README.md): 개인적인 목표 작성
Redish03 Oct 22, 2025
551fa67
feat: 사용자에게서 자동차 이름과 움직일 횟수를 입력받는다
Redish03 Oct 22, 2025
1782755
test(InputViewTest.kt): InputView의 반환형이 String인지 확인하는 테스트 작성
Redish03 Oct 22, 2025
dde0d83
test(InputViewTest.kt): 테스트 함수 명 수정
Redish03 Oct 22, 2025
036fc23
Merge pull request #1 from Redish03/feature/input_from_user
Redish03 Oct 22, 2025
4750c2f
docs(README.md): 구현할 기능 작성
Redish03 Oct 22, 2025
b866161
test(InputValidatorTest.kt): 조건을 만족시키지 않는 입력이 들어올 때 InputValidator.kt…
Redish03 Oct 22, 2025
f52d3e1
feat(InputValidator.kt): 자동차 이름에 대해서 조건에 맞지 않는 값이 들어오면 IllegalArgumen…
Redish03 Oct 22, 2025
60c13d8
refactor(InputValidator.kt): 매직넘버 상수화
Redish03 Oct 22, 2025
34e8bc3
Merge pull request #2 from Redish03/feature/car_name_validation
Redish03 Oct 23, 2025
e5602eb
test(InputValidatorTest.kt): 입력받은 입력 횟수가 정수형이 조건에 맞는지 확인하는 테스트를 작성
Redish03 Oct 23, 2025
8c98e4e
feat(InputValidator.kt): 입력받은 이동 횟수가 조건에 부합하는지 확인한다
Redish03 Oct 23, 2025
3e0bd2d
Merge pull request #3 from Redish03/feature/total_movement_input_vali…
Redish03 Oct 23, 2025
19093f3
feat(Separator.kt): `,`를 기준으로 이름들을 분리한다
Redish03 Oct 23, 2025
d1d605e
test(SeparatorTest.kt): `,`를 기준으로 이름들을 분리하는지 확인한다
Redish03 Oct 23, 2025
4aa104c
test(SeparatorTest.kt): `,`를 기준으로 이름들을 분리하는지 확인한다
Redish03 Oct 23, 2025
2b0634d
feat(RacingController.kt): Controller에 자동차 이름 처리 로직 추가
Redish03 Oct 23, 2025
2220522
Merge pull request #4 from Redish03/feature/separate_name
Redish03 Oct 23, 2025
9f475b3
docs(README.md): 랜덤 넘버 생성 구현 내용 작성
Redish03 Oct 23, 2025
5b895b2
feat(RandomNumberGenerator.kt): 랜덤 넘버 생성
Redish03 Oct 23, 2025
1266eb8
Merge pull request #5 from Redish03/feature/generate_random_number
Redish03 Oct 23, 2025
b171b90
docs(README.md): 구현할 기능 작성
Redish03 Oct 23, 2025
e82cdec
test(RandomNumberGeneratorTest.kt): 0~9까지의 숫자가 반환되는지 테스트
Redish03 Oct 23, 2025
3c0c132
feat(NumberGenerator.kt): 수를 생성하는 규칙 작성
Redish03 Oct 23, 2025
8e8d659
feat(RandomNumberGenerator.kt): 무작위 수를 생성하는 RandomNumberGenerator를 Nu…
Redish03 Oct 23, 2025
bec9b3d
chore: NumberGenerator 사용 이유 작성
Redish03 Oct 23, 2025
b7f4cae
Merge pull request #6 from Redish03/feature/generate_random_number
Redish03 Oct 23, 2025
2ba8669
feat(RacingController.kt): 컨트롤러에 반복 횟수 입력받는 로직 추가
Redish03 Oct 24, 2025
ee522df
docs(README.md): 구현할 기능 추가
Redish03 Oct 24, 2025
2990ecf
feat(Car.kt): 자동차 객체 추가 및 조건 부합 시 전진 로직 추가
Redish03 Oct 24, 2025
22ad36c
feat(Car.kt): name 변수화
Redish03 Oct 24, 2025
3dabc1a
feat(RacingController.kt): 레이싱 로직 흐름 작성
Redish03 Oct 24, 2025
30d5e35
Merge pull request #7 from Redish03/feature/run_race
Redish03 Oct 24, 2025
fcfabf1
docs(README.md): 구현할 기능 작성
Redish03 Oct 26, 2025
e21bb04
feat(OutputView.kt): 출력 메서드 추가
Redish03 Oct 26, 2025
ddfca01
feat(RacingController.kt): 컨트롤러에 출력 로직 추가
Redish03 Oct 26, 2025
7762fc7
feat(RacingController.kt): 컨트롤러에 우승자 출력 하도록 로직 수정
Redish03 Oct 26, 2025
2473b01
Merge pull request #8 from Redish03/feature/print_to_user
Redish03 Oct 26, 2025
53a3558
docs(README.md): 구현할 기능 작성
Redish03 Oct 26, 2025
e9f7f5f
test(JudgeTest.kt): 우승자 판별 테스트 실행
Redish03 Oct 26, 2025
5fa8a3a
test(RacingControllerTest.kt): 테스트 코드에 의존성 주입
Redish03 Oct 26, 2025
1f2e800
feat(Judge.kt): 우승자 선별 기능 구현
Redish03 Oct 26, 2025
32f95aa
feat(RacingController.kt): controller에 우승자 선정 로직 추가
Redish03 Oct 26, 2025
5469556
Merge pull request #9 from Redish03/feature/judge_winner
Redish03 Oct 26, 2025
55ad183
fix(InputValidator.kt): 잘못된 로직 고침
Redish03 Oct 26, 2025
157c6cb
chore(OutputView.kt): 한 줄 띄우기 추가
Redish03 Oct 26, 2025
67f62cc
feat(Application.kt): 레이싱 로직 추가 및 의존성 주입
Redish03 Oct 26, 2025
d0de7d4
refactor: 코드를 Kotlin 컨벤션에 맞게 수정
Redish03 Oct 26, 2025
dcff15e
refactor(RacingController.kt): 코드를 읽기 쉽게 메서드 분리
Redish03 Oct 27, 2025
ee6b9ea
test(RacingControllerTest.kt): 라운드 횟수 입력 테스트
Redish03 Oct 27, 2025
b1eda47
test(Separator.kt): Separator가 ,를 기준으로 구분하는지 확인하는 테스트 추가
Redish03 Oct 27, 2025
1e93d97
refactor: 필요없는 라이브러리 및 의존성 삭제
Redish03 Oct 27, 2025
f94302c
refactor: joinToString으로 코드 단순화
Redish03 Oct 27, 2025
b7d94f4
refactor: repeat으로 코드를 더 읽기 쉽게 만들기
Redish03 Oct 27, 2025
d519bd1
refactor(Car.kt): Car에 NumberGenerator로 의존성 주입
Redish03 Oct 27, 2025
69c7cf1
refactor(RacingController.kt): Car 생성자를 통한 의존성 주입
Redish03 Oct 27, 2025
f05f9bb
fix(JudgeTest.kt): Car 생성 버그 수정
Redish03 Oct 27, 2025
d1b406b
refactor(NumberGenerator.kt): 불필요한 주석 제거
Redish03 Oct 27, 2025
a780a09
fix(OutputView.kt): 우승자 출력할 때 공백 추가
Redish03 Oct 27, 2025
130c158
refactor(RacingController.kt): racing 할 때 for 문 대신 repeat문으로 대체
Redish03 Oct 27, 2025
732324d
docs(README.md): 중복하는 이름이 있다면 IllegalArgumentException을 발생시키는 기능 구현 예정
Redish03 Oct 27, 2025
d091238
feat(InputValidator.kt): 중복하는 이름이 있다면 IllegalArgumentException을 발생시키는…
Redish03 Oct 27, 2025
3aaf317
feat(InputValidator.kt): 필요없는 중복된 이름 발생시 예외발생 기능 제거
Redish03 Oct 27, 2025
1c4933f
refactor(Separator.kt): kotlin 컨벤션에 맞게 코드 수정
Redish03 Oct 27, 2025
afe15d9
Merge pull request #10 from Redish03/develop
Redish03 Oct 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,107 @@
# kotlin-racingcar-precourse

## 자동차 경주 게임

---
> 주어진 횟수동안 n대의 자동차가 전진 또는 멈추는 프로그램으로, 최종적으로 가장 멀리 간 자동차가 우승한다

- 각 자동차들은 이름을 부여할 수 있다.
- 전진하는 자동차를 출력할 때, 자동차의 이름을 같이 출력한다.
- 자동차 이름은 쉼표로 구분한다. (이름은 5자 이내)
- 사용자가 몇 번의 이동을 할 것인지 입력한다.
- 전진 조건은 0-9 사이의 무작이 값을 구한 후 무작위 값이 4이상일 경우이다.
- 경주 게임을 완료한 후 누가 우승했는지 알려준다. (우승자는 한 명 이상일 수 있다)
- 사용자가 잘못된 입력값을 입력할 경우 `IllegalArgumentExcetpion`을 발생시킨다.

### 실행 결과 예시
```text
경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)
>>> pobi,woni,jun
시도할 횟수는 몇 회인가요?
>>> 5

실행 결과
pobi : -
woni :
jun : -

pobi : --
woni : -
jun : --

pobi : ---
woni : --
jun : ---

pobi : ----
woni : ---
jun : ----

pobi : -----
woni : ----
jun : -----

최종 우승자 : pobi, jun
```

## 구현할 기능

---
### view
**`InputManager`**
- 사용자에게서 자동차들의 이름을 부여받는다
- 사용자에게서 이동의 횟수를 부여받는다

**OutputManager**
- 가이드를 출력한다
- 라운드별 실행 결과를 출력한다
- 최종 우승자를 출력한다

### model
**InputValidator**
- 입력하는 값들에 대해서 조건을 만족하는지 확인한다
- 차 이름이 조건을 만족하는지 확인
- 이름은 `String` 형태로 주어진다
- 이름은 5자가 넘지 않는다
- 공백이 주어지는지 확인한다
- `null`이 아니다
- 이동 횟수가 조건을 만족하는지 확인
- 이동 횟수는 `null`이 아니다
- 이동 횟수는 양수로 주어진다

**(interface)NumberGenerator**
- 수를 생성하는 `generate`에 대한 규칙이다

**RandomNumberGenerator**
- 0-9까지의 수 중 랜덤 숫자를 내놓는다
- `camp.nextstep.edu.missionutils.Randoms`의 `pickNumberInRange()`를 사용한다

**Car**
- 생성될 때 입력한 이름이 조건에 맞는지 확인한다
- 랜덤 번호를 생성하고 이 번호가 기준을 넘는다면 앞으로 전진한다

**Judge**
- 각 차의 거리를 비교해 우승자를 가린다

### Controller

- 자동차의 이름을 받아 생성한다
- 반복할 횟수를 입력받는다
- 차를 움직이고, 매 라운드 결과를 출력한다
- 우승자를 가리고 출력한다

## 개인적인 목표

---
1. MVC 패턴을 지키겠다
2. 의미있는 이름을 짓도록 노력하겠다
3. 각 함수들이 하는 일이 작고 명확하게 하겠다
4. 좋은 커밋 메세지를 작성하겠다
1. 제목과 본문을 한 줄 띄워 작성
2. 제목은 영문 기준 50자 이내로 작성
3. 제목 첫 글자는 대문자
4. 제목 끝에 `.`금지
5. 본문은 영문 기준 72자마다 줄바꿈
6. 본문은 `무엇을`, `왜`에 중점으로 작성
5. 테스트 코드를 작성 할 때 `AssertTrue`, `AssertThat` 등 기존에 써본 것들 뿐만 아니라 새로운 것들을 시도하겠다
6. Kotlin 컨벤션을 지키면서 작성하겠다
6 changes: 5 additions & 1 deletion src/main/kotlin/racingcar/Application.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package racingcar

import racingcar.domain.Judge
import racingcar.view.InputView
import racingcar.view.OutputView

fun main() {
// TODO: 프로그램 구현
RacingController(InputView(), OutputView(), Judge()).run()
}
25 changes: 25 additions & 0 deletions src/main/kotlin/racingcar/InputValidator.kt
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

코드 패키징을 분리하신 걸 보니, InputValidatorSeparator가 루트 패키지에 있네요!
두 코드는 입력 처리 관련이니 view패키지에 있는 게 자연스럽다고 생각해요! 성우님이 이렇게 배치하신 이유가 궁금해요😊

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package racingcar

class InputValidator {
fun validateCarName(carNames: List<String>) {
require(carNames.isNotEmpty()) { "자동차의 이름을 입력하셔야 합니다" }
for (carName in carNames) {
require(carName.isNotBlank()) { "자동차의 이름은 빈칸일 수 없습니다" }
require(carName.length <= MAX_CAR_NAME_LENGTH) { "자동차의 이름은 5글자 이하여야 합니다" }
Comment on lines +7 to +8
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

위의 부분은 Car.kt와 중복되는 검증 로직 같아요.

// Car.kt - init
require(name.length < MAX_NAME_LENGTH) { "자동차 이름의 길이는 5자 이하여야 합니다" }
require(!name.contains(" ")) { "자동차 이름엔 공백이 포함되면 안됩니다" }

그리고 조건 또한 <<=로 차이가 존재해요 🤔
한곳에서만 검증을 책임지는게 명확한 책임분리라고 생각이 드는데 성우님은 어떻게 생각하시나요?

}
}

fun validateTotalMovement(totalMovement: String) {
require(totalMovement.isNotEmpty()) { "이동 횟수를 입력하셔야 합니다" }
require(totalMovement.isNotBlank()) { "빈칸으로 주어지면 안됩니다" }
try {
totalMovement.toInt()
} catch (e: NumberFormatException) {
throw IllegalArgumentException("정수를 입력하셔야 합니다")
}
Comment on lines +15 to +19
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

toInt() 대신 toIntOrNull()을 사용하면 require로 통일할 수 있을 것 같아요!

Suggested change
try {
totalMovement.toInt()
} catch (e: NumberFormatException) {
throw IllegalArgumentException("정수를 입력하셔야 합니다")
}
require(totalMovement.toIntOrNull() != null) { "정수를 입력하셔야 합니다" }

}

companion object {
const val MAX_CAR_NAME_LENGTH = 5
}
}
62 changes: 62 additions & 0 deletions src/main/kotlin/racingcar/RacingController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package racingcar

import racingcar.domain.Car
import racingcar.domain.Judge
import racingcar.domain.RandomNumberGenerator
import racingcar.view.InputView
import racingcar.view.OutputView

class RacingController(val inputView: InputView, val outputView: OutputView, val judge: Judge) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

RacingController가 아래처럼 책임을 가지고 있는것 같아요.

class RacingController {
    fun handleCarNames()      // 입력 + 검증
    fun handleRepeatTime()    // 입력 + 검증
    fun race()                // 게임 실행 + 출력
    private fun moveCar()     // 게임 로직
    private fun printCurrentCarState()  // 출력
}

위의 리뷰와 같은 부분이 있지만, Controller가 입력처리, 검증, 게임 실행, 출력까지 모두 담당하고 있어요. 게임 로직은 별도의 도메인 클래스로 분리하는게 좋다고 생각이 들어요! 제가 생각하는 Controller의 역할은 흐름을 제어하는 것인데요🤔 성우님은 어떤 역할이라고 생각하시나요?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

추가로 RacingController의 생성자 파라미터가 public 프로퍼티로 노출되고 있어요. 혹시 의도하신 걸까요? 🧐

제가 생각하기에는 외부에서 접근하지 않아도 되는 파라미터라고 생각해요. 때문에 private val을 사용하는게 좋다고 생각이 들어요 😊

fun run() {
val carNames = handleCarNames()
val repeatTime = handleRepeatTime()

outputView.printTextOfResult()
val raceResult = race(carNames, repeatTime.toInt())
val winners = judge.judgeWinner(raceResult)
outputView.printWinner(winners)
}

fun handleCarNames(): List<String> {
outputView.printCarNameInputGuide()
val inputFromUser = inputView.getCarNameFromUser()
val carNames = Separator().separateName(inputFromUser)
InputValidator().validateCarName(carNames)
return carNames
}

fun handleRepeatTime(): String {
outputView.printMovementTimeInputGuide()
val repeatTime = inputView.getMovementTimeFromUser()
InputValidator().validateTotalMovement(repeatTime)
return repeatTime
}
Comment on lines +20 to +33
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

// fun handleCarNames
val carNames = Separator().separateName(inputFromUser)
InputValidator().validateCarName(carNames)

// fun handleRepeatTime
InputValidator().validateTotalMovement(repeatTime)

위의 함수에서 해당하는 부분을 보면 매번 함수 호출시마다 새로운 객체를 생성하고 있어요. 호출 횟수만큼 힙 메모리에 새 객체가 생성되면서 프로그램 실행 중 최소 2번 객체가 생성되는것 같아요.
상태가 있다면 유의미하지만 상태가 없는 클래스인데도 매선 새 객체를 만드는게 타당한가?라는 의문이 들어요🤔

또한 다른 개발자가 해석하면서 새 인스턴스를 매번 만드는 코드를 보면

"혹시 상태를 가지나?"

라는 의문을 줄 수 있다고 보이는데요😊 싱글톤이나 companion object를 활용하는 방법은 고려 해보셨나요? 저는 싱글톤으로 같은 인스턴스를 사용하는게 좋다고 생각해요!


fun race(carNames: List<String>, repeatTime: Int): List<Car> {
val cars: List<Car> = carNames.map { carName ->
Car(
name = carName,
RandomNumberGenerator()
)
}

repeat(repeatTime) {
moveCar(cars)
printCurrentCarState(cars)
}
return cars
}
Comment on lines +35 to +48
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

"함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라"

라는 요구사항이 있었는데요! 제가 생각했을때는 지금 race() 함수가 여러가지 일을 하고 있어요.
자동차 생성과 게임을 진행,출력,반환 을 담당하고 있는데, 최소한 자동차 생성과 게임 진행은 분리되어야 한다고 생각이 들어요! 어떻게 생각하시나요?🤔


private fun moveCar(cars: List<Car>) {
for (car in cars) {
car.moveForward()
}
}

private fun printCurrentCarState(cars: List<Car>) {
for (car in cars) {
outputView.printCurrentLocation(car.name, car.distance)
}
println()
}
Comment on lines +56 to +61
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

README에서 "MVC 패턴을 지키겠다" 라고 해주셨는데 Controller에서 println()을 직접 출력하는건 디자인패턴에 맞지 않은 것 같아요!
제가 생각하기에는 출력은 View의 담당이라고 생각해요. OutputView를 통해서 출력을 하는게 맞다고 생각이 드네요😁

}
9 changes: 9 additions & 0 deletions src/main/kotlin/racingcar/Separator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package racingcar

class Separator {
fun separateName(names: String): List<String> = names.split(NAME_SEPARATOR)

companion object {
const val NAME_SEPARATOR = ","
}
}
23 changes: 23 additions & 0 deletions src/main/kotlin/racingcar/domain/Car.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package racingcar.domain

class Car(val name: String, val numberGenerator: NumberGenerator) {
var distance: Int = INITIAL_CAR_DISTANCE

init {
require(name.length < MAX_NAME_LENGTH) { "자동차 이름의 길이는 5자 이하여야 합니다" }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

요구사항은 "이름은 5자 이하만 가능"으로 되어있어요.
<연산자로 인해서 4자까지만 허용되는것 같아요😊

require(!name.contains(" ")) { "자동차 이름엔 공백이 포함되면 안됩니다" }
}

fun moveForward() {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Car에 대한 테스트 코드가 없는 것 같아요!

"JUnit 5와 AssertJ를 이용하여 정리한 기능 목록이 정상적으로 작동하는지 테스트 코드로 확인한다"

moveForward()같은 핵심 로직은 테스트를 해야 한다고 생각합니다. 혹시 제가 놓친거라면 죄송해요🥲

val randomNumber = numberGenerator.generateNumber()
if (randomNumber >= MOVE_FORWARD_CONDITION_NUMBER) {
distance++
}
}
Comment on lines +11 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

바뀔수있는 요구사항이라 추상화 시켰으면 좋을꺼 같아요!


companion object {
private const val MAX_NAME_LENGTH = 5
private const val INITIAL_CAR_DISTANCE = 0
private const val MOVE_FORWARD_CONDITION_NUMBER = 4
}
}
19 changes: 19 additions & 0 deletions src/main/kotlin/racingcar/domain/Judge.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package racingcar.domain

class Judge {
fun judgeWinner(gameResult: List<Car>): List<String> {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

승자 판단 알고리즘 상당히 인상적이네요!

val winners = mutableListOf<String>()

var maxDistance = 0
for (cars in gameResult) {
if (cars.distance == maxDistance) winners.add(cars.name)
else if (cars.distance > maxDistance) {
winners.clear()
winners.add(cars.name)
maxDistance = cars.distance
}
}

return winners
}
Comment on lines +4 to +18
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Judge의 알고리즘이 복잡하고 indent depth 를 넘어갈 가능성이 있어 보여요😊
Kotlin의 함수형 스타일을 사용하면 더 간결하게 표현이 될 것 같아요.

Suggested change
fun judgeWinner(gameResult: List<Car>): List<String> {
val winners = mutableListOf<String>()
var maxDistance = 0
for (cars in gameResult) {
if (cars.distance == maxDistance) winners.add(cars.name)
else if (cars.distance > maxDistance) {
winners.clear()
winners.add(cars.name)
maxDistance = cars.distance
}
}
return winners
}
fun judgeWinner(gameResult: List<Car>): List<String> {
val maxDistance = gameResult.maxOf { it.distance }
return gameResult.filter { it.distance == maxDistance }
.map { it.name }
}

이렇게 작성해도 좋을 것 같습니다💪🏻

}
5 changes: 5 additions & 0 deletions src/main/kotlin/racingcar/domain/NumberGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package racingcar.domain

interface NumberGenerator {
fun generateNumber(): Int
}
Comment on lines +3 to +5
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이 인터페이스를 만들어 주시고 테스트에서는 활용해주시지 않은 것 같아요.
저라면 아래처럼 테스트용 구현체를 만들어서 테스트를 구현 하는편인데요!

class FixedNumberGenerator(private val value: Int) : NumberGenerator {
    override fun generateNumber(): Int = value
}

성우님은 val car = Car("test", RandomNumberGenerator()) 처럼 테스트를 실제 랜덤을 사용하신 이유가 궁금해요 😊

12 changes: 12 additions & 0 deletions src/main/kotlin/racingcar/domain/RandomNumberGenerator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package racingcar.domain

import camp.nextstep.edu.missionutils.Randoms

class RandomNumberGenerator : NumberGenerator {
override fun generateNumber(): Int = Randoms.pickNumberInRange(RANDOM_MIN_NUMBER, RANDOM_MAX_NUMBER)

companion object {
const val RANDOM_MIN_NUMBER = 0
const val RANDOM_MAX_NUMBER = 9
}
}
8 changes: 8 additions & 0 deletions src/main/kotlin/racingcar/view/InputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package racingcar.view

import camp.nextstep.edu.missionutils.Console

class InputView {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

object 말고 class 를 사용하신 이유가 있으실까요??

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

마찬가지로 저도 궁금합니다!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

object 키워드는 싱글톤 객체를 생성하는 것으로 알고 있습니다! 물론 object가 생성까지 같이해서 좋지만, class로 선언하는 게 확장성적인 측면에서 좀 더 좋다고 생각했거든요..ㅎㅎ 나중에 인터페이스를 사용하게 되거나 사용자에게서 콘솔로 받는 것이 아닌 등등의 상황을 생각했었습니다.
물론 지금 같은 상황에선 싱글톤 객체로 생성하는 것도 좋은 방법인 것 같습니다! 사용자에게서 입력을 한 곳에서 받아오니 싱글톤디 좋은 것 같기도 해요. 사실 구현할 땐 생각하지도 못했는데 허를 찔린 기분이네요,,ㅎㅎ

fun getCarNameFromUser(): String = Console.readLine()
fun getMovementTimeFromUser(): String = Console.readLine()
}
29 changes: 29 additions & 0 deletions src/main/kotlin/racingcar/view/OutputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package racingcar.view

class OutputView {
fun printCarNameInputGuide() {
println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)")
}

fun printMovementTimeInputGuide() {
println("시도할 횟수는 몇 회인가요?")
}

fun printTextOfResult() {
println()
println("실행 결과")
}

fun printCurrentLocation(name: String, distance: Int) {
print("$name : ")
repeat(distance) {
print("-")
}
println()
}
Comment on lines +17 to +23
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

코틀린이면 한스트링에 변수를 삽입 가능했을텐데 나누신 이유가 뭘까요?? 가독성 부분에서 나누신걸까요?


fun printWinner(winners: List<String>) {
val result = winners.joinToString(separator = ", ")
print("최종 우승자 : $result")
}
}
60 changes: 60 additions & 0 deletions src/test/kotlin/racingcar/InputValidatorTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package racingcar

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows

class InputValidatorTest {
@Test
fun `자동차의 이름이 없다면 IllegalArgumentException을 발생시킨다`() {
// given
val carName = mutableListOf<String>()

// when & then
assertThrows<IllegalArgumentException> { InputValidator().validateCarName(carName) }
}
Comment on lines +7 to +14
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

이 테스트에서 변경하지 않는 리스트를 mutableListOf로 만들 필요는 없다고 생각해요! emptyList() 혹은 listOf()를 사용하는 게 의도를 더 명확히 전달 할 것 같아요😁


@Test
fun `자동차의 이름이 5글자를 초과하면 IllegalArgumentException을 발생시킨다`() {
// given
val carName = listOf<String>("thisistest", "arguments")

// when & then
assertThrows<IllegalArgumentException> { InputValidator().validateCarName(carName) }
}

@Test
fun `자동차의 이름이 공백이라면 IllegalArgumentException을 발생시킨다`() {
// given
val carName = listOf<String>("pluto", " ")

// when & then
assertThrows<IllegalArgumentException> { InputValidator().validateCarName(carName) }
}

@Test
fun `이동 횟수가 정수가 아니면 IllegalArgumentException을 발생시킨다`() {
// given
val repeatTime = "String"

// when & then
assertThrows<IllegalArgumentException> { InputValidator().validateTotalMovement(repeatTime) }
}

@Test
fun `이동 횟수가 빈칸이라면 IllegalArgumentException을 발생시킨다`() {
// given
val repeatTime = ""

// when & then
assertThrows<IllegalArgumentException> { InputValidator().validateTotalMovement(repeatTime) }
}

@Test
fun `이동 횟수가 공백으로 주어지면 IllegalArgumentException을 발생시킨다`() {
// given
val repeatTime = " "

// when & then
assertThrows<IllegalArgumentException> { InputValidator().validateTotalMovement(repeatTime) }
}
}
Loading