-
Notifications
You must be signed in to change notification settings - Fork 419
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
Step3 : 자동차 경주 #1512
base: sendkite
Are you sure you want to change the base?
Step3 : 자동차 경주 #1512
Changes from all commits
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,15 @@ | ||
# kotlin-racingcar | ||
# kotlin-racingcar | ||
|
||
## 기능 요구사항 | ||
- 초간단 자동차 경주 게임을 구현한다. | ||
- 주어진 횟수 동안 n대의 자동차는 전진 또는 멈출 수 있다. | ||
- 사용자는 몇 대의 자동차로 몇 번의 이동을 할 것인지를 입력할 수 있어야 한다. | ||
- 전진하는 조건은 0에서 9 사이에서 random 값을 구한 후 random 값이 4 이상일 경우이다. | ||
- 자동차의 상태를 화면에 출력한다. 어느 시점에 출력할 것인지에 대한 제약은 없다. | ||
|
||
|
||
## 구현 | ||
|
||
- [ ] 자동차는 게임에 참가할 수 있다. | ||
- [ ] 자동차는 전진 또는 멈출 수 있다. | ||
- [ ] 자동차는 0~9 사이 무작위 값을 방아 4이상이면 1칸 전진 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package racingcar | ||
|
||
class Car( | ||
var position: Int = 0, | ||
var moveCondition: RandomNumberHolder | ||
) { | ||
fun move() { | ||
if (moveCondition.getRandomNumber() >= 4) { | ||
Comment on lines
+5
to
+8
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. 추상화의 범위를 어떻게 잡을 지 정답은 없으나 여기서는 랜덤 넘버를 반환하는 것 보다는 움직일지 말지 여부를 결정하는 부분을 추상화하는게 어땠을까 하는 생각이 드네요. 이 부분에 대해서도 고민해보시면 좋겠습니다. 정답은 없으므로 변경하진 않으셔도 됩니다. |
||
position++ | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package racingcar | ||
|
||
data class InputView( | ||
var gameCount: Int = 0, | ||
var cars: List<Car> = listOf() | ||
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. 아래 프로퍼티로 정의할 필요가 없을 것 같다고 말씀드리긴 했는데요, 가변 프로퍼티라 추가로 코멘트 남깁니다.
Comment on lines
+4
to
+5
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. 둘 다 프로퍼티로 정의할 필요가 있을까요? 필요한 값을 바로 반환할 순 없을까요? |
||
) { | ||
fun inputGameCount() { | ||
println("시도할 회수는 몇회인가요?") | ||
val input = readln().toInt() | ||
validate(input) | ||
this.gameCount = input | ||
} | ||
|
||
fun inputCarNumber() { | ||
println("자동차 대수는 몇 대 인가요?") | ||
val input = readln().toInt() | ||
validate(input) | ||
|
||
this.cars = List(input) { | ||
Car(moveCondition = RandomNumberHolderImpl()) | ||
} | ||
} | ||
|
||
fun validate(input: Int) { | ||
require(input > 0) { throw IllegalArgumentException("음수는 입력할 수 없습니다.") } | ||
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. require은 조건에 따른 결과가 맞지 않을 경우 IllegalArgumentException을 반환하기 때문에 예외 메시지만 남겨주시면 될 것 같습니다. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package racingcar | ||
|
||
class OutputView { | ||
fun printResult() { | ||
println("실행 결과") | ||
} | ||
|
||
fun printPosition(position: Int): String { | ||
return "-".repeat(position) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package racingcar | ||
|
||
class RacingGame( | ||
val gameCount: Int, | ||
val cars: List<Car> = listOf() | ||
) { | ||
fun start() { | ||
OutputView().printResult() | ||
repeat(gameCount) { | ||
moveCars() | ||
println() | ||
} | ||
} | ||
|
||
private fun moveCars() { | ||
cars.forEach { car -> | ||
car.move() | ||
println(OutputView().printPosition(car.position)) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package racingcar | ||
|
||
interface RandomNumberHolder { | ||
|
||
fun getRandomNumber(): Int | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package racingcar | ||
|
||
import java.util.Random | ||
|
||
class RandomNumberHolderImpl : RandomNumberHolder { | ||
override fun getRandomNumber(): Int { | ||
return Random().nextInt(0, 9) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package racingcar | ||
|
||
import io.kotest.matchers.shouldBe | ||
import org.assertj.core.api.Assertions.assertThatThrownBy | ||
import org.junit.jupiter.api.Test | ||
import org.junit.jupiter.params.ParameterizedTest | ||
import org.junit.jupiter.params.provider.CsvSource | ||
import org.junit.jupiter.params.provider.ValueSource | ||
import racingcar.mock.MoveForwardCondition | ||
import racingcar.mock.StayCondition | ||
|
||
class RacingGameTest { | ||
|
||
@Test | ||
fun `성공 - 게임횟수 자동차 수를 입력`() { | ||
|
||
val inputView = InputView( | ||
gameCount = 5, | ||
cars = listOf( | ||
Car(moveCondition = RandomNumberHolderImpl()), | ||
Car(moveCondition = RandomNumberHolderImpl()), | ||
Car(moveCondition = RandomNumberHolderImpl()) | ||
|
||
) | ||
) | ||
|
||
val racingGame = RacingGame( | ||
inputView.gameCount, | ||
inputView.cars | ||
) | ||
|
||
racingGame.gameCount shouldBe 5 | ||
racingGame.cars.size shouldBe 3 | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(ints = [-12, 0, -1]) | ||
fun `실패 - 0 이하의 게임횟수, 자동차 수 입력`(input: Int) { | ||
|
||
assertThatThrownBy { InputView().validate(input) } | ||
.isInstanceOf(IllegalArgumentException::class.java) | ||
.hasMessage("음수는 입력할 수 없습니다.") | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(ints = [4, 5, 6, 7, 8, 9]) | ||
fun `무작위 수 4 이상이면 자동차 전진`(num : Int) { | ||
|
||
val car = Car(moveCondition = RandomNumberHolderImpl()).apply { | ||
moveCondition = object : RandomNumberHolder { | ||
override fun getRandomNumber(): Int { | ||
return num | ||
} | ||
} | ||
} | ||
|
||
car.move() | ||
car.position shouldBe 1 | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(ints = [1, 2, 3]) | ||
fun `무작위 수 4 이하면 정지`(num : Int) { | ||
|
||
val car = Car(moveCondition = RandomNumberHolderImpl()).apply { | ||
moveCondition = object : RandomNumberHolder { | ||
override fun getRandomNumber(): Int { | ||
return num | ||
} | ||
} | ||
} | ||
|
||
car.move() | ||
car.position shouldBe 0 | ||
} | ||
|
||
@ParameterizedTest | ||
@CsvSource(value = ["1:-","2:--","3:---","4:----"], delimiter = ':' ) | ||
fun `자동차 이동 거리 출력`(input: Int, expected: String) { | ||
OutputView().printPosition(input) shouldBe expected | ||
} | ||
|
||
@ParameterizedTest | ||
@ValueSource(ints = [1, 2, 3, 4, 5]) | ||
fun `게임 결과 출력`(gameCount: Int) { | ||
val inputView = InputView( | ||
gameCount = gameCount, | ||
cars = listOf( | ||
Car(moveCondition = MoveForwardCondition()), | ||
Car(moveCondition = StayCondition()), | ||
) | ||
) | ||
|
||
val racingGame = RacingGame( | ||
inputView.gameCount, | ||
inputView.cars | ||
) | ||
racingGame.start() | ||
|
||
racingGame.cars[0].position shouldBe gameCount | ||
racingGame.cars[1].position shouldBe 0 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package racingcar.mock | ||
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은 호출에 대한 기대를 명세하고 내용에 따라 동작하도록 프로그래밍 된 객체를 의미합니다. 현재는 호출에 대한 기대를 정의하지 않고(어떤 메서드 호출 시 어떤 값이 반환될 지 정의하는 것) 정해진 값을 리턴하고 있으므로 Mock보다는 Stub에 가깝지 않나 하는 생각이 드네요. 자세한 내용은 아래 문서 한번 참고 부탁드립니다. https://tecoble.techcourse.co.kr/post/2020-09-19-what-is-test-double/ |
||
|
||
import racingcar.RandomNumberHolder | ||
|
||
class MoveForwardCondition: RandomNumberHolder { | ||
|
||
override fun getRandomNumber(): Int { | ||
return 4 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package racingcar.mock | ||
|
||
import racingcar.RandomNumberHolder | ||
|
||
class StayCondition: RandomNumberHolder { | ||
override fun getRandomNumber(): Int { | ||
return 3 | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,4 +18,4 @@ class StringTest { | |
assertThat("\n".isBlank()).isTrue() | ||
assertThat("""\n""".isBlank()).isFalse() | ||
} | ||
} | ||
} |
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.
지금과 같은 구조라면 move를 호출하지 않고도 외부에서 가변인 position의 값을 바꿀 수 있을 것 같네요. 이 부분에 대해 고민해보시면 좋겠습니다.