Skip to content

[Step5] 자동차 경주(리팩토링) #945

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

Open
wants to merge 52 commits into
base: eedys1234
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
533fe6b
<feat> : 문자열 계산기 기능 개발
Nov 5, 2022
3e96967
<refactor> : 리팩토링(Operator 클래스 분리)
Nov 5, 2022
34561c3
<refactor> : 계산 로직 리팩토링
Nov 5, 2022
7459316
<refactor> : 검증로직 리팩토링, 표현 클래스 분리
Nov 5, 2022
cf15a54
<refactor> : 피드백 적용
Nov 8, 2022
ab71f37
<feat> : 임시 적용
Nov 12, 2022
400f6b2
<feat> : racing game 개발
Nov 12, 2022
76b3eed
<feat> : 문자열 계산기 기능 개발
Nov 5, 2022
61eca99
<refactor> : 리팩토링(Operator 클래스 분리)
Nov 5, 2022
2f8da74
<refactor> : 계산 로직 리팩토링
Nov 5, 2022
34bf39a
<feat> : 임시 적용
Nov 12, 2022
c06f4b7
<feat> : racing game 개발
Nov 12, 2022
187496c
[step2] : 피드백 적용
Nov 16, 2022
a3fb8fe
conflict: 충돌해결
Nov 16, 2022
f1912b8
Merge branch 'step3' of https://github.com/eedys1234/kotlin-racingcar…
Nov 16, 2022
bb0eff8
요구사항 README.md에 정리
Nov 16, 2022
ebe9a9a
[feat]: step3 기능 개발
Nov 16, 2022
0205960
피드백 적용
Nov 17, 2022
1a5e1de
피드백 적용
Nov 17, 2022
61c89f5
<refactor> : 피드백적용
eedys Nov 19, 2022
9ca62d4
<docs> : 요구사항 README.md에 정의
eedys Nov 19, 2022
20eacb8
<docs> : 요구사항 추가
eedys Nov 19, 2022
8dfbb0c
<feat> : 자동차에 이름 부여
eedys Nov 19, 2022
7f70845
<feat> : 자동차의 이름이 5글자보다 클경우 예외처리
eedys Nov 19, 2022
289f67e
<feat> : 매 횟수마다 실행결과를 출력한다.
eedys Nov 19, 2022
8255e01
<refactor> : 실행결과 출력 수정
eedys Nov 19, 2022
8f355a9
<feat> : 우승자 선정 기능
eedys Nov 19, 2022
301f988
<docs> : README.md 수정
eedys Nov 19, 2022
73e4b7e
<fix> : 우승자 추출 로직 수정
eedys Nov 19, 2022
95ab2cf
<feat> : 피드백 적용
eedys Nov 21, 2022
00d1fa1
<refactor> : 피드백 적용
Nov 8, 2022
1252ad6
<feat> : racing game 개발
Nov 12, 2022
ad82787
<refactor> : 검증로직 리팩토링, 표현 클래스 분리
Nov 5, 2022
663a612
[feat]: step3 기능 개발
Nov 16, 2022
ccd1f78
피드백 적용
Nov 17, 2022
e966f5d
<refactor> : 피드백적용
eedys Nov 19, 2022
bb2be54
<docs> : 요구사항 README.md에 정의
eedys Nov 19, 2022
68d6e54
<docs> : 요구사항 추가
eedys Nov 19, 2022
a2dc93d
<feat> : 자동차에 이름 부여
eedys Nov 19, 2022
c7bf837
<feat> : 자동차의 이름이 5글자보다 클경우 예외처리
eedys Nov 19, 2022
21b4160
<feat> : 매 횟수마다 실행결과를 출력한다.
eedys Nov 19, 2022
20bec37
<refactor> : 실행결과 출력 수정
eedys Nov 19, 2022
c6634b0
<feat> : 우승자 선정 기능
eedys Nov 19, 2022
00a1c52
<docs> : README.md 수정
eedys Nov 19, 2022
9590f95
<fix> : 우승자 추출 로직 수정
eedys Nov 19, 2022
05bde2e
<feat> : 피드백 적용
eedys Nov 21, 2022
81075ed
<refactor> : 피드백 적용
Nov 8, 2022
fa7afb9
Merge branch 'step2' into step4
eedys Nov 21, 2022
0969d56
<conflict> : rebase 과정에서 충돌해결
eedys Nov 21, 2022
e25f175
<refactor> : 피드백 적용
eedys Nov 25, 2022
7798f1a
<feat> : 피드백 적용
eedys Nov 27, 2022
a353de4
<feat> : 의존관계 분리
eedys Nov 27, 2022
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
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
# kotlin-racingcar

- [o] 자동차 생성
- [o] 자동차는 4~9 범위 사이일때 움직일 수 있다.
- [o] 자동차는 0~3 범위 사이에는 움직일 수 없다.
- [o] 자동차에 이름을 부여할 수 있다.
- [o] 자동차의 이름은 5글자를 초과할 수 없다.
- [o] 자동차의 이름이 5글자를 초과하면 예외를 발생시킨다.
- [o] 자동차의 움직임
- [o] 자동차는 4~9 범위 사이일때 움직일 수 있다.
- [o] 자동차는 0~3 범위 사이에는 움직일 수 없다.
- [o] 무작위 값은 0~9 범위 사이이다.
- [o] 클라이언트로부터 값을 입력받을 수 있다.
- [o] 실행결과를 출력한다.
- [o] 클라이언트로부터 값을 입력받을 수 있다.
- [o] 자동차의 이름은 쉼표(,)로 구분할 수 있다.
- [o] 실행결과를 출력한다.
- [o] 매 횟수마다 출력한다.
- [o] 매 횟수마다 전진하는 자동차의 이름도 출력한다.
- [o] 최종 우승자를 출력하며, 우승자는 한명 이상이다.
- [o] 의존관계
- [o] Domain -> View X
- [o] View -> Domain O
9 changes: 8 additions & 1 deletion src/main/kotlin/racing/application/RacingGameService.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package racing.application

import racing.domain.RacingGame
import racing.view.InputView
import racing.view.ResultView

class RacingGameService {

fun main() {
val racingCarNames = InputView.requireRacingCarNames()
val numberOfGames = InputView.requireNumberOfGames()

val racingGame = RacingGame()
racingGame.start()
val winCars = racingGame.start(racingCarNames, numberOfGames)
ResultView.printRacingGameGuideText()
ResultView.printRacingGameWinner(winCars)
}
}
10 changes: 9 additions & 1 deletion src/main/kotlin/racing/domain/Car.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package racing.domain

class Car(position: Int = 0) {
class Car(val name: String, position: Int = 0) {

var position = position
private set

init {
require(name.length <= CAR_NAME_LIMIT) {
"자동차의 이름은 ${CAR_NAME_LIMIT}글자를 초과할 수 없습니다."
}
}

fun move(num: Int) {
if (num >= FORWARD_MOVE) {
position++
Expand All @@ -13,5 +19,7 @@ class Car(position: Int = 0) {

companion object {
const val FORWARD_MOVE: Int = 4
const val CAR_NAME_LIMIT: Int = 5
fun produce(name: String, position: Int = 0) = Car(name, position)
}
}
4 changes: 2 additions & 2 deletions src/main/kotlin/racing/domain/CarFactory.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package racing.domain
class CarFactory {

companion object {
fun create(count: Int): Cars {
return Cars(count)
fun create(names: Array<String>): Cars {
return Cars(names)
}
}
}
18 changes: 8 additions & 10 deletions src/main/kotlin/racing/domain/Cars.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
package racing.domain

class Cars(count: Int) : Iterable<Car> {

private val list: List<Car>

init {
list = List(count) {
Car()
}
}
class Cars(private val list: List<Car>) : Iterable<Car> {
constructor(names: Array<String>) : this(names.map { Car.produce(it) })

fun count() = list.size

fun race(movable: () -> Int) {
list.forEach {
car ->
car.move(movable.invoke())
car.move(movable())
}
}

private fun isWinnerPosition() = list.maxOf { it.position }

fun isWinner() = list.filter { it.position == isWinnerPosition() }

override fun iterator(): Iterator<Car> = list.iterator()
}
19 changes: 8 additions & 11 deletions src/main/kotlin/racing/domain/RacingGame.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
package racing.domain

import racing.ui.InputView
import racing.ui.ResultView
import racing.view.ResultView

class RacingGame {

fun start() {
fun start(carNames: Array<String>, numberOfGames: Int): List<Car> {

val carCount = InputView.requireCarsCount()
val cars = CarFactory.create(carCount)
var numberOfGames = InputView.requireNumberOfGames()
val cars = CarFactory.create(carNames)

while (numberOfGames-- > 0) {
cars.race {
RandomGenerator.generate()
}
for (i in 1..numberOfGames) {
cars.race { RandomGenerator.generate() }
ResultView.printRacingGameResult(ResultView.toRaceResult(cars))
}

ResultView.printGameResult(ResultStatistics(cars).toResult())
val winner = Winner(cars)
return winner.win()
}
}
2 changes: 1 addition & 1 deletion src/main/kotlin/racing/domain/RandomGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ package racing.domain
class RandomGenerator {

companion object {
fun generate(): Int = (0..9).random()
fun generate() = (0..9).random()
}
}
16 changes: 0 additions & 16 deletions src/main/kotlin/racing/domain/ResultStatistics.kt

This file was deleted.

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

class Winner(private val cars: Cars) {

fun win(): List<Car> = cars.isWinner()
}
19 changes: 0 additions & 19 deletions src/main/kotlin/racing/ui/InputView.kt

This file was deleted.

11 changes: 0 additions & 11 deletions src/main/kotlin/racing/ui/ResultView.kt

This file was deleted.

23 changes: 23 additions & 0 deletions src/main/kotlin/racing/view/InputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package racing.view

class InputView {

companion object {
private const val SEPARATOR = ","

fun requireRacingCarNames(): Array<String> {
println("경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분")
val readString = readString()
return readString.split(SEPARATOR).toTypedArray()
}

fun requireNumberOfGames(): Int {
println("시도할 횟수는 몇 회인가요?")
return readNum()
}

private fun readNum() = readln().toInt()

private fun readString() = readln()
}
}
32 changes: 32 additions & 0 deletions src/main/kotlin/racing/view/ResultView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package racing.view

import racing.domain.Car
import racing.domain.Cars

class ResultView {

companion object {
fun toRaceResult(cars: Cars): String {
return cars.map { "${it.name} : ${toMark(it)}" }.joinToString(separator = System.lineSeparator()).trimIndent()
}

private fun toMark(car: Car): String {
return MARK.repeat(car.position)
}

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

fun printRacingGameResult(result: String) {
println(result)
}

fun printRacingGameWinner(cars: List<Car>) {
val winner = cars.joinToString { it.name }
println("${winner}가 최종 우승했습니다.")
}

private const val MARK = "-"
}
}
29 changes: 21 additions & 8 deletions src/test/kotlin/racing/CarTest.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package racing

import org.assertj.core.api.AssertionsForInterfaceTypes
import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat
import org.assertj.core.util.Arrays
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import racing.domain.CarFactory
Expand All @@ -10,28 +12,39 @@ import racing.domain.Cars
class CarTest {

@Test
fun `자동차 생성`() {
val cars: Cars = CarFactory.create(3)
AssertionsForInterfaceTypes.assertThat(cars.count()).isEqualTo(3)
fun `자동차를 생성할 때 자동차의 이름은 5글자 이하이다`() {
val names = Arrays.array("lee", "kkoks", "amery")
val cars: Cars = CarFactory.create(names)
assertThat(cars.count()).isEqualTo(3)
}

@Test
fun `자동차를 생성할 때 자동차의 이름은 5글자를 초과한다`() {
val names = Arrays.array("lee", "kkokss", "amery")
assertThrows<IllegalArgumentException> {
CarFactory.create(names)
}
}

@ParameterizedTest
@ValueSource(ints = [4, 9])
fun `자동차 이동`(num: Int) {
val cars: Cars = CarFactory.create(3)
val names = Arrays.array("lee", "kkoks", "amery")
val cars: Cars = CarFactory.create(names)
for (car in cars) {
car.move(num)
AssertionsForInterfaceTypes.assertThat(car.position).isEqualTo(1)
assertThat(car.position).isEqualTo(1)
}
}

@ParameterizedTest
@ValueSource(ints = [0, 3])
fun `자동차 이동실패`(num: Int) {
val cars: Cars = CarFactory.create(3)
val names = Arrays.array("lee", "kkoks", "amery")
val cars: Cars = CarFactory.create(names)
for (car in cars) {
car.move(num)
AssertionsForInterfaceTypes.assertThat(car.position).isEqualTo(0)
assertThat(car.position).isEqualTo(0)
}
}
}
42 changes: 39 additions & 3 deletions src/test/kotlin/racing/RacingGameTest.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,55 @@
package racing

import org.assertj.core.api.AssertionsForInterfaceTypes.assertThat
import org.assertj.core.util.Arrays
import org.junit.jupiter.api.Test
import racing.domain.Car
import racing.domain.CarFactory
import racing.domain.Cars
import racing.domain.ResultStatistics
import racing.domain.Winner
import racing.view.ResultView

class RacingGameTest {

@Test
fun `실행결과 출력`() {
val cars: Cars = CarFactory.create(2)
val names = Arrays.array("lee", "kkoks")
val cars: Cars = CarFactory.create(names)
for (car in cars) {
car.move(4)
}
assertThat(ResultStatistics(cars).toResult()).isEqualTo("-${System.lineSeparator()}-")

val result = """
lee : -
kkoks : -
""".trimIndent()
assertThat(ResultView.toRaceResult(cars)).isEqualTo(result)
}

@Test
fun `Racing Game 단독 우승자 출력(차가 한대일경우)`() {
val kkoksCar = Car.produce("kkoks", 3)
val winner = Winner(Cars(listOf(kkoksCar)))
val result = winner.win()
assertThat(result).contains(kkoksCar)
}

@Test
fun `Racing Game 단독 우승자 출력`() {
val leeCar = Car.produce("lee", 5)
val kkoksCar = Car.produce("kkoks", 3)
val winner = Winner(Cars(listOf(leeCar, kkoksCar)))
val result = winner.win()
assertThat(result).contains(leeCar)
}

@Test
fun `Racing Game 공동 우승자 출력`() {
val leeCar = Car.produce("lee", 5)
val kkoksCar = Car.produce("kkoks", 5)
val tesyCar = Car.produce("tesy", 3)
val winner = Winner(Cars(listOf(leeCar, kkoksCar, tesyCar)))
val result = winner.win()
assertThat(result).contains(leeCar, kkoksCar)
}
}