-
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
[Step5]자동차 경주(리팩터링) #936
base: choi-ys
Are you sure you want to change the base?
[Step5]자동차 경주(리팩터링) #936
Changes from 8 commits
a59f19b
b6ac70f
7a4fee4
9f3930b
9c82457
0effa6b
c6fd39d
10710ad
753cac7
def429b
ebca741
107cc52
352368f
754af09
b024f5a
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 |
---|---|---|
|
@@ -4,14 +4,10 @@ private const val CAR_ID_DELIMITER = "-" | |
private const val MOVE_CRITERIA = 4 | ||
|
||
class Car(val name: String) { | ||
private var randomNumbers: RandomNumbers = RandomNumbers() | ||
var distance = 0 | ||
var distance: Int = 0 | ||
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. distance는 외부에서 접근해서 값을 변경할 수 있겠네요! |
||
|
||
fun addRandomNumber(randomNumber: Int) = randomNumbers.add(randomNumber) | ||
|
||
fun race(currentRoundIndex: Int) { | ||
val randomNumberByCurrentRound: Int = randomNumbers[currentRoundIndex] | ||
if (isMove(randomNumberByCurrentRound)) { | ||
fun race(randomNumber: Int) { | ||
if (isMove(randomNumber)) { | ||
distance++ | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,23 @@ package step3.racingcar.domain | |
|
||
class Cars private constructor(private val elements: List<Car>) { | ||
fun elements(): List<Car> = elements | ||
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. elements 프로퍼티를 공개하는 것과 element() 메서드를 따로 만드는 것은 큰 차이가 없어보이네요 :) |
||
|
||
fun size(): Int = elements.size | ||
operator fun get(index: Int) = elements[index] | ||
|
||
operator fun get(index: Int): Car = elements[index] | ||
|
||
fun winnerNames(): String = | ||
findWinnerNames().joinToString(WINNER_NAME_JOINING_SEPARATOR) | ||
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. 승자들의 이름들을 , 로 묶어내는 일은 뷰의 일로 보이네요 :) |
||
|
||
private fun findWinnerNames(): List<String> = | ||
elements() | ||
.filter { it.distance == findMaxDistance() } | ||
.map { it.name } | ||
|
||
private fun findMaxDistance(): Int = elements().maxOf { it.distance } | ||
|
||
companion object { | ||
fun of(elements: List<Car>) = Cars(elements) | ||
private const val WINNER_NAME_JOINING_SEPARATOR = ", " | ||
fun of(elements: List<Car>): Cars = Cars(elements) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package step3.racingcar.domain | ||
|
||
interface RandomNumber { | ||
fun value(): Int | ||
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. 하위 구현체가 "랜덤 숫자 생성기" 이니, 상위 인터페이스는, "숫자 생성기" 정도가 어떨까요? |
||
|
||
companion object { | ||
const val RANGE_START = 1 | ||
const val RANGE_END = 9 | ||
} | ||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,30 @@ | ||
package step3.racingcar.service | ||
|
||
import step3.racingcar.domain.Car | ||
import step3.racingcar.domain.Cars | ||
import step3.racingcar.domain.PlayInfo | ||
import step3.racingcar.domain.Winners | ||
import step3.racingcar.domain.RandomNumber | ||
import step3.racingcar.view.ResultView.Companion.printRoundResult | ||
import step3.racingcar.view.ResultView.Companion.printWinner | ||
|
||
class RacingCarService { | ||
class RacingCarService(private val randomNumber: RandomNumber) { | ||
fun play(playInfo: PlayInfo) { | ||
repeat(playInfo.totalRound) { | ||
playEachRound(it, playInfo.cars) | ||
} | ||
printWinner(Winners.of(playInfo.cars)) | ||
printWinner(playInfo.cars) | ||
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. 도메인 로직을 수행하는 Service는 View의 일을 가지고 있네요! |
||
} | ||
|
||
private fun playEachRound(currentRoundIndex: Int, cars: Cars) { | ||
fun playEachRound(currentRoundIndex: Int, cars: Cars) { | ||
cars.elements().forEach { | ||
it.race(currentRoundIndex) | ||
val randomNumber = randomNumber.value() | ||
it.race(randomNumber) | ||
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. cars에서 직접 값을 꺼내서 메서드를 호출하기 보다, cars에게 메시지를 던져보는 것은 어떨까요? |
||
} | ||
printRoundResult(currentRoundIndex, cars) | ||
} | ||
|
||
fun playEachRoundByCar(car: Car) { | ||
val randomNumber = randomNumber.value() | ||
car.race(randomNumber) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,11 @@ | ||
package step3.racingcar.utils | ||
|
||
import step3.racingcar.domain.Car | ||
import step3.racingcar.domain.Cars | ||
import step3.racingcar.domain.RandomNumber | ||
import step3.racingcar.domain.RandomNumber.Companion.RANGE_END | ||
import step3.racingcar.domain.RandomNumber.Companion.RANGE_START | ||
|
||
object RandomNumberGenerator { | ||
private const val RANGE_START = 1 | ||
private const val RANGE_END = 9 | ||
|
||
fun generateRandomNumberToCarByRound(cars: Cars, totalRound: Int) { | ||
cars.elements().forEach { | ||
generateRandomNumberToEachCar(it, totalRound) | ||
} | ||
} | ||
class RandomNumberGenerator : RandomNumber { | ||
override fun value(): Int = generate() | ||
|
||
private fun generate(): Int = (RANGE_START..RANGE_END).random() | ||
|
||
private fun generateRandomNumberToEachCar(car: Car, totalRound: Int) { | ||
repeat(totalRound) { | ||
car.addRandomNumber(generate()) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,27 +2,24 @@ package step3.racingcar.domain | |
|
||
import io.kotest.core.spec.style.BehaviorSpec | ||
import io.kotest.matchers.shouldBe | ||
import step3.racingcar.fixture.CarFixtureGenerator | ||
|
||
class CarTest : BehaviorSpec({ | ||
given("경주에 참가하는 자동차 한대에 4이상의 숫자가 주어지고") { | ||
val currentRoundIndex = 0 | ||
val givenCar = CarFixtureGenerator.난수를_가지는_차량_생성("참가 차량", 4) | ||
val givenCar = Car("참가 차량") | ||
|
||
`when`("경주를 진행하면") { | ||
givenCar.race(currentRoundIndex) | ||
givenCar.race(4) | ||
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. given과 when이 조금 섞여있어 보여요. |
||
then("현재 차량의 주행거리를 1만큼 누적한다.") { | ||
givenCar.distance shouldBe 1 | ||
} | ||
} | ||
} | ||
|
||
given("경주에 참가하는 자동차 한대에 3이하의 숫자가 주어지고") { | ||
val currentRoundIndex = 0 | ||
val givenCar = CarFixtureGenerator.난수를_가지는_차량_생성("참가 차량", 3) | ||
val givenCar = Car("참가 차량") | ||
|
||
`when`("경주를 진행하면") { | ||
givenCar.race(currentRoundIndex) | ||
givenCar.race(3) | ||
then("현재 차량의 주행거리는 누적되지 않는다.") { | ||
givenCar.distance shouldBe 0 | ||
} | ||
|
@@ -33,22 +30,19 @@ class CarTest : BehaviorSpec({ | |
val car = Car("참가 차량") | ||
|
||
`when`("첫번째 라운드에 4가 주어지면") { | ||
car.addRandomNumber(4) | ||
car.race(0) | ||
car.race(4) | ||
then("차량 전진 횟수가 1 증가한다.") { | ||
car.distance shouldBe 1 | ||
} | ||
} | ||
`when`("두번째 라운드에 3이 주어지면") { | ||
car.addRandomNumber(3) | ||
car.race(1) | ||
car.race(3) | ||
then("차량의 전진 횟수는 증가하지 않는다.") { | ||
car.distance shouldBe 1 | ||
} | ||
} | ||
`when`("세번째 라운드에 6이 주어지면") { | ||
car.addRandomNumber(6) | ||
car.race(2) | ||
car.race(6) | ||
then("차량 전진 횟수가 1 증가한다.") { | ||
car.distance shouldBe 2 | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package step3.racingcar.domain | ||
|
||
import io.kotest.core.spec.style.BehaviorSpec | ||
import io.kotest.matchers.shouldBe | ||
|
||
internal class CarsTest : BehaviorSpec({ | ||
given("경주를 완료한 자동차 객체 목록이 주어지고") { | ||
val 첫_번째_차량 = 경주를_완료한_차량_생성("첫 번째 차량", 1, 1, 1, 4, 4) | ||
val 두_번째_차량 = 경주를_완료한_차량_생성("두 번째 차량", 1, 1, 4, 4, 4) | ||
val 세_번째_차량 = 경주를_완료한_차량_생성("세 번째 차량", 4, 4, 4, 1, 1) | ||
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. Car의 생성자에 distance 초기값을 넣게 만들어보는 것은 어떨까요? |
||
|
||
`when`("우승자 객체를 생성하면") { | ||
val 참가_차량_목록 = listOf(첫_번째_차량, 두_번째_차량, 세_번째_차량) | ||
val given = Cars.of(참가_차량_목록) | ||
then("우승한 차량들의 이름을 반환한다.") { | ||
given.winnerNames() shouldBe "두 번째 차량, 세 번째 차량" | ||
} | ||
} | ||
} | ||
}) | ||
|
||
private fun 경주를_완료한_차량_생성(carName: String, vararg randomNumbers: Int): Car { | ||
val car = Car(carName) | ||
randomNumbers.forEach { | ||
car.race(it) | ||
} | ||
return car | ||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package step3.racingcar.fixture | ||
|
||
import step3.racingcar.domain.RandomNumber | ||
|
||
class TestRandomNumberGenerator(private val testValue: Int = DEFAULT_TEST_VALUE) : RandomNumber { | ||
override fun value(): Int = testValue | ||
|
||
companion object { | ||
private const val DEFAULT_TEST_VALUE = 1 | ||
} | ||
} |
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.
Service 하위의 모든 객체에서 이제 생성되는 숫자에 대해 개발자가 제어할 수 있겠네요 :) 👍👍