-
Notifications
You must be signed in to change notification settings - Fork 410
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
[Step4] - 자동차 경주(우승자) #909
base: riyenas0925
Are you sure you want to change the base?
Changes from all commits
41e8e25
d0bed0a
0a9c80e
530906b
57b1c77
79f0885
9ab2acd
3dcd8df
82e4a62
7c6de49
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,16 +1,19 @@ | ||
package racingcar | ||
|
||
class Application | ||
|
||
fun main() { | ||
val (cars, roundCount) = InputView() | ||
val cars = InputView.readCars() | ||
val roundCount = InputView.readRoundCount() | ||
|
||
val racingGame = RacingGame( | ||
cars = cars, | ||
numberGenerator = RacingCarNumberGenerator() | ||
) | ||
|
||
repeat(roundCount) { | ||
val drivingCars = racingGame.round() | ||
ResultView.print(drivingCars) | ||
ResultView.printLocation(drivingCars) | ||
} | ||
|
||
val winners = racingGame.judge() | ||
ResultView.printWinner(winners) | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,16 +1,27 @@ | ||||||||||||||||||||||||||
package racingcar | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
class Car( | ||||||||||||||||||||||||||
private var location: Int = 0, | ||||||||||||||||||||||||||
private var _location: Int = DEFAULT_CAR_LOCATION, | ||||||||||||||||||||||||||
val name: String, | ||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||
fun move(randNumber: Int) { | ||||||||||||||||||||||||||
val rule = MovingJudgeRule.judge(randNumber) | ||||||||||||||||||||||||||
val strategy = rule.strategy() | ||||||||||||||||||||||||||
init { | ||||||||||||||||||||||||||
require(isValidLength(name)) { "글자수는 5자를 넘을 수 없어요" } | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
val location | ||||||||||||||||||||||||||
get() = _location | ||||||||||||||||||||||||||
Comment on lines
+7
to
+12
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. Kotlin Coding Convention에 따르면 클래스는 아래의 순서대로 포맷을 지키라고 하고있어요! 😃
Suggested change
|
||||||||||||||||||||||||||
fun move(number: Int) { | ||||||||||||||||||||||||||
val rule = MovingJudgeRule.judge(number) | ||||||||||||||||||||||||||
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. 이 부분에 대해서도 다시 고민해봤는데 (꼭 강한 결합 때문만이 아니라) |
||||||||||||||||||||||||||
val strategy = rule.strategy | ||||||||||||||||||||||||||
_location = strategy.move(_location) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
Comment on lines
+4
to
+17
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. backing properties를 사용해서 구현했는데 클래스 내부에서 location과 _location이 헷갈리기도 하고 무엇보다 변수 앞에 _ 가 붙는게 익숙치 않다는 느낌을 받았어요. 리뷰어님은 어떤 방식을 선호하시는지 궁금해요~! 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. 저 같은 경우에는 저는 backing properties를 잘 사용하지 않는 편이에요. 그 이유는 대부분은 |
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
location = strategy.move(location) | ||||||||||||||||||||||||||
private fun isValidLength(name: String): Boolean { | ||||||||||||||||||||||||||
return name.length <= VALID_CAR_NAME_LENGTH | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
fun currentLocation(): Int { | ||||||||||||||||||||||||||
return location | ||||||||||||||||||||||||||
companion object { | ||||||||||||||||||||||||||
private const val DEFAULT_CAR_LOCATION = 0 | ||||||||||||||||||||||||||
private const val VALID_CAR_NAME_LENGTH = 5 | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,26 @@ | ||
package racingcar | ||
|
||
data class InputView( | ||
val cars: List<Car> = readCars("자동차 대수는 몇 대인가요?"), | ||
val roundCount: Int = readRoundCount("시도할 횟수는 몇 회인가요?"), | ||
) { | ||
companion object { | ||
private fun readCars(message: String): List<Car> { | ||
println(message) | ||
object InputView { | ||
fun readCars(): List<Car> { | ||
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.
|
||
println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분).") | ||
val names = parseCarName(readln()) | ||
|
||
val list = mutableListOf<Car>() | ||
repeat(readln().toInt()) { | ||
list.add(Car()) | ||
} | ||
|
||
return list | ||
val list = mutableListOf<Car>() | ||
for (name in names) { | ||
list.add( | ||
Car(name = name) | ||
) | ||
} | ||
Comment on lines
+8
to
13
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. 아래와 같이 변경해볼 수 있을 것 같아요. 😃 return return names.map { Car(name = it) } |
||
|
||
private fun readRoundCount(message: String): Int { | ||
println(message) | ||
return readln().toInt() | ||
} | ||
return list | ||
} | ||
|
||
fun readRoundCount(): Int { | ||
println("시도할 횟수는 몇 회인가요?") | ||
return readln().toInt() | ||
} | ||
|
||
private fun parseCarName(input: String): List<String> { | ||
return input.split(",") | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -2,15 +2,15 @@ package racingcar | |||||
|
||||||
enum class MovingJudgeRule( | ||||||
private val expression: (Int) -> (Boolean), | ||||||
private val strategy: MovingStrategy | ||||||
val strategy: MovingStrategy | ||||||
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. 3단계 미션에서 코멘트 드렸었는데 반영이 안된 것 같아요. 🥲 먼저 그리고
Suggested change
|
||||||
) { | ||||||
|
||||||
ADVANCE_RULE( | ||||||
expression = { number -> number >= FORWARD_STRATEGY_BOUND }, | ||||||
expression = { it >= FORWARD_STRATEGY_BOUND }, | ||||||
strategy = MovingStrategy.ADVANCE, | ||||||
), | ||||||
STOP_RULE( | ||||||
expression = { false }, | ||||||
expression = { it < FORWARD_STRATEGY_BOUND }, | ||||||
strategy = MovingStrategy.STOP, | ||||||
), | ||||||
; | ||||||
|
@@ -19,10 +19,6 @@ enum class MovingJudgeRule( | |||||
return expression(number) | ||||||
} | ||||||
|
||||||
fun strategy(): MovingStrategy { | ||||||
return strategy | ||||||
} | ||||||
|
||||||
companion object { | ||||||
private const val FORWARD_STRATEGY_BOUND = 4 | ||||||
|
||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,11 @@ interface NumberGenerator { | |
|
||
class RacingCarNumberGenerator : NumberGenerator { | ||
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. NumberGenerator 객체는 상태를 가지는 것도 아니기 때문에 굳이 객체로 만들 필요가 없을 것 같아요. 🤔 |
||
override fun rand(): Int { | ||
return (0..9).random() | ||
return (RAND_NUMBER_START_BOUND..RAND_NUMBER_END_BOUND).random() | ||
} | ||
|
||
companion object { | ||
private const val RAND_NUMBER_START_BOUND = 0 | ||
private const val RAND_NUMBER_END_BOUND = 9 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,11 @@ class RacingGame( | |
private val cars: List<Car>, | ||
private val numberGenerator: NumberGenerator, | ||
) { | ||
|
||
init { | ||
requireCarExist(cars) | ||
} | ||
|
||
fun round(): List<Car> { | ||
for (car in cars) { | ||
val randNumber = numberGenerator.rand() | ||
|
@@ -12,4 +17,15 @@ class RacingGame( | |
|
||
return cars | ||
} | ||
|
||
fun judge(): List<Car> { | ||
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. judge라는 메서드 네이밍이 자주 보이는 것 같아요. 🤔 |
||
val maxLocation = cars.maxOf { it.location } | ||
return cars.filter { | ||
it.location == maxLocation | ||
} | ||
} | ||
|
||
private fun requireCarExist(cars: List<Car>) { | ||
require(cars.isNotEmpty()) { "레이싱 게임에서 참여하는 자동차는 1대 이상이어야 합니다." } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -1,14 +1,17 @@ | ||||||||||
package racingcar | ||||||||||
|
||||||||||
class ResultView { | ||||||||||
companion object { | ||||||||||
fun print(cars: List<Car>) { | ||||||||||
for (car in cars) { | ||||||||||
val location = car.currentLocation() | ||||||||||
repeat(location) { print("-") } | ||||||||||
println() | ||||||||||
} | ||||||||||
object ResultView { | ||||||||||
fun printLocation(cars: List<Car>) { | ||||||||||
for (car in cars) { | ||||||||||
val location = car.location | ||||||||||
print(car.name + " : ") | ||||||||||
repeat(location) { print("-") } | ||||||||||
println() | ||||||||||
Comment on lines
+7
to
9
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. 아래와 같이 바꿔볼 수 있을 것 같아요. 😃
Suggested change
|
||||||||||
} | ||||||||||
println() | ||||||||||
} | ||||||||||
|
||||||||||
fun printWinner(cars: List<Car>) { | ||||||||||
println("${cars.joinToString { it.name }}가 최종 우승했습니다.") | ||||||||||
} | ||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,19 +4,33 @@ import io.kotest.core.spec.style.StringSpec | |
import io.kotest.matchers.shouldBe | ||
import io.mockk.every | ||
import io.mockk.mockk | ||
import org.junit.jupiter.api.assertThrows | ||
import java.lang.IllegalArgumentException | ||
|
||
class RacingGameTest : StringSpec({ | ||
"레이싱 게임은 매 횟수마다 자동차의 현재 위치를 보여줘요" { | ||
val mockNumberGenerator = mockk<NumberGenerator>() | ||
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. 이번 미션에서는 mock 라이브러리 없이 테스트 코드를 작성해보시면 좋을 것 같아요. 😃 |
||
every { mockNumberGenerator.rand() } returns 0 | ||
|
||
val racingGame = RacingGame( | ||
cars = listOf(Car(0)), | ||
cars = listOf(Car(0, "car")), | ||
numberGenerator = mockNumberGenerator | ||
) | ||
|
||
val car = racingGame.round()[0] | ||
|
||
car.currentLocation() shouldBe 0 | ||
car.location shouldBe 0 | ||
} | ||
|
||
"레이싱 게임에 참여하는 자동차는 1대 이상이어야 합니다." { | ||
assertThrows<IllegalArgumentException> { | ||
val mockNumberGenerator = mockk<NumberGenerator>() | ||
every { mockNumberGenerator.rand() } returns 0 | ||
|
||
val racingGame = RacingGame( | ||
cars = listOf(), | ||
numberGenerator = mockNumberGenerator | ||
) | ||
} | ||
} | ||
}) |
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.
객체가 생성되는 시점에 유효성을 체크하도록 만드셨군요? 👍👍
개인적으로 지금의 경우에는 Validation 로직이 간단하므로 메서드로 추출할 필요없이 바로 명시하는 것이 더 가독성이 좋은 것 같아요. 🙂
추가적으로 예외 메시지를 조금 더 명확하게 적어보면 어떨까요? 😃