Skip to content
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

[부나] 코틀린 자동차 경주 제출합니다. #47

Merged
merged 58 commits into from
Feb 11, 2023
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
4d15ac7
docs: 기능 목록 초기 작성
tmdgh1592 Feb 7, 2023
2fb4547
feat: 최종 우승자 산출 기능 구현
tmdgh1592 Feb 7, 2023
d85e64e
feat: 자동차 이름들 입력 기능 구현
tmdgh1592 Feb 7, 2023
69fe601
feat: 시도 횟수 입력 기능 구현
tmdgh1592 Feb 7, 2023
ca51779
refactor: 조건 숫자, 에러문구 상수화
tmdgh1592 Feb 7, 2023
37462f6
feat: 자동차 랜덤 전진 기능 구현
tmdgh1592 Feb 7, 2023
8808d37
test: 테스트 커밋
tmdgh1592 Feb 8, 2023
1d86cdd
feat: 실행 결과 출력 기능 구현
tmdgh1592 Feb 8, 2023
e1f5fc1
feat: 최종 우승자 산출 기능 구현
tmdgh1592 Feb 8, 2023
118d2cb
docs: 기능 목록 추가
tmdgh1592 Feb 8, 2023
b826b0e
feat: 우승자 출력 기능 구현
tmdgh1592 Feb 8, 2023
972d412
feat: 기능 통합 구현
tmdgh1592 Feb 8, 2023
7b51f8c
test: Car class 테스트 초기 작성
tmdgh1592 Feb 8, 2023
b16841c
test: Validator class 테스트 초기 작성
tmdgh1592 Feb 8, 2023
e724948
test: Validator class 시도 횟수 입력 테스트 초기 작성
tmdgh1592 Feb 8, 2023
1381ae5
test: RacingService class 객체 생성 테스트 작성
tmdgh1592 Feb 8, 2023
d88ea80
feat: 고정 시드 랜덤 유틸 구현
tmdgh1592 Feb 8, 2023
7e42178
refactor: 랜덤 시작, 종료 범위, 이동 확률 상수화
tmdgh1592 Feb 8, 2023
0edf628
test: 최종 우승자 산출 테스트 코드 구현
tmdgh1592 Feb 9, 2023
1ca0cda
test: 최종 우승자 산출 테스트 코드 구현
tmdgh1592 Feb 9, 2023
4d5d28b
test: 자동차 생성 예외 테스트 코드 구현
tmdgh1592 Feb 9, 2023
89bfc95
test: 최종 우승자 산출 예외 테스트 코드 구현
tmdgh1592 Feb 9, 2023
fe11d89
test: 전체 기능 테스트 코드 구현
tmdgh1592 Feb 9, 2023
5d67e4e
feat: 공백 지우는 클래스 구현
tmdgh1592 Feb 9, 2023
3571323
refactor: 메서드명, 길이 10자 이내로 제한하도록 리팩토링
tmdgh1592 Feb 9, 2023
39f2cdd
test: validator 테스트 코드 매개변수 수정
tmdgh1592 Feb 9, 2023
3e46229
docs: .gitkeep 파일 제거
tmdgh1592 Feb 10, 2023
c34e4cc
docs: .editorconfig 파일 마지막에 개행 문자 추가
tmdgh1592 Feb 10, 2023
890bef6
refactor: createCars() 메서드 반환값 표기
tmdgh1592 Feb 10, 2023
8945701
refactor: ktlint에 맞춰 각 파일의 마지막 줄에 개행 문자 추가
tmdgh1592 Feb 10, 2023
3aa878a
refactor: Car의 toString() 메서드 재정의 제거하고 name 프로퍼티에 접근할 수 있도록 수정
tmdgh1592 Feb 10, 2023
74f7af6
refactor: Car 클래스의 getPositionAsDash() 메서드를 제거하고 position 프로퍼티에 접근할 수…
tmdgh1592 Feb 10, 2023
3ab052f
refactor: Random 유틸 클래스를 제거하고 Service에서 로직 구현
tmdgh1592 Feb 10, 2023
ce2e1bb
refactor: BlankRemover 유틸 클래스를 제거하고 Extensions 파일에서 처리하도록 수정
tmdgh1592 Feb 10, 2023
efd5d0c
fix: getRandomProbabilityInRange() 메서드의 Max bound 범위를 1 감소
tmdgh1592 Feb 10, 2023
9282f50
refactor: Dimens, Strings 파일에서 관리하던 상수를 필요한 클래스에서 관리하도록 수정
tmdgh1592 Feb 10, 2023
35dff55
test: testCarMovement() 메서드명을 testCarMovement()로 변경
tmdgh1592 Feb 10, 2023
e45ab6b
refactor: 불필요한 주석 및 코드 제거
tmdgh1592 Feb 10, 2023
4f9ba23
test: 자동차 객체 생성 테스트 코드 구현
tmdgh1592 Feb 10, 2023
07dcf8e
refactor: ApplicationKtTest 테스트 코드 분리하고 Given-When-Then 표기법으로 변경
tmdgh1592 Feb 10, 2023
983fd98
refactor: CarTest 네이밍을 Given-When-Then 표기법으로 변경
tmdgh1592 Feb 10, 2023
e735d17
refactor: RacingServiceTest 네이밍을 Given-When-Then 표기법으로 변경
tmdgh1592 Feb 10, 2023
8a24159
refactor: 자동차 이름의 길이를 Car 클래스 생성시 검증하도록 변경
tmdgh1592 Feb 10, 2023
d9c1cde
refactor: round 범위를 Round 래퍼 클래스에서 검증하도록 변경
tmdgh1592 Feb 10, 2023
3250ce8
refactor: Round 범위 입력값의 숫자 여부 검증을 InputView에서 하도록 변경하고 일반화
tmdgh1592 Feb 10, 2023
c41adbe
feat: CarRepository 클래스 구현
tmdgh1592 Feb 10, 2023
aaca844
feat: 자동차 중복 검증을 CarRepository에서 하도록 변경
tmdgh1592 Feb 10, 2023
385e9b7
feat: 우승자 산출시 Car의 compareTo() 메서드로 비교하던 부분을 각 position 프로퍼티와 비교하도록 변경
tmdgh1592 Feb 10, 2023
7012826
refactor: Dimens 파일에서 관리하던 상수를 필요한 클래스에서 관리하도록 수정
tmdgh1592 Feb 10, 2023
810050b
refactor: getWinners() 메서드 매개변수 제거
tmdgh1592 Feb 10, 2023
0924fe7
refactor: Round 객체 생성시 검증을 require 메서드를 사용하도록 변경
tmdgh1592 Feb 10, 2023
1f76b4e
test: 라운드 객체 생성 테스트 코드 구현
tmdgh1592 Feb 10, 2023
3783a31
fix: CarRepositry에서 car 객체 삽입시 검증을 위해 비교하는 기준을 이름으로 변경하여 에러 수정
tmdgh1592 Feb 10, 2023
f3d42d2
test: 자동차 moveCount 테스트에서 주어진 자동차 객체를 insert하도록 변경
tmdgh1592 Feb 10, 2023
a2441a2
refactor: 불필요한 상수 제거
tmdgh1592 Feb 10, 2023
3966720
refactor: getRandomProbabilityInRange() 메서드명 변경
tmdgh1592 Feb 10, 2023
f1d7a52
refactor: Car의 move() 메서드에 이동 조건값 추가
tmdgh1592 Feb 10, 2023
5a27b6d
test: Car 클래스의 move() 메서드 테스트 코드 구현
tmdgh1592 Feb 10, 2023
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
16 changes: 16 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = false
max_line_length = 120
tab_width = 4

[*.{kt,kts}]
disabled_rules=import-ordering
# java.* 패키지를 의존하는 경우 IntelliJ의 Orgarnize Import 기능으로는 알파벳 순서대로 import 구문을 정렬할 수 없다.
# 이는 ktlint의 import-ordering 규칙과 맞지 않는다.

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

너무 시원시원하네요😭👏
마지막에 개행 문자를 추가하는 것에 대해 의문이 있었는데 일부 환경에서는 EOF이 없으면 컴파일 에러가 발생하는군요!

추가로 .editorconfig를 수동으로 작성해서 추가해야 하는 줄 알았는데 Cmd + N 단축키로 쉽게 생성할 수 있다는 것도 알게 되었습니다!
감사합니다~🙏

4 changes: 3 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ dependencies {
testImplementation("org.junit.jupiter", "junit-jupiter", "5.8.2")
testImplementation("org.assertj", "assertj-core", "3.22.0")
testImplementation("io.kotest", "kotest-runner-junit5", "5.2.3")

testImplementation(kotlin("test"))
}

tasks {
Expand All @@ -29,4 +31,4 @@ tasks {
ktlint {
verbose.set(true)
}
}
}
36 changes: 36 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
### 기능 목록
-
- 입력
- 자동차 이름들 입력
- [x] 입력받은 문자열을 콤마를 기준으로 split
- [x] 앞뒤 공백 제거
- [X] 각각 0 이하 6자 이상 시 예외 발생 후 재입력
- [x] 중복이 있을 시 예외 발생 후 재입력
- 시도 횟수 입력
- [x] 숫자 형태 확인
- [x] 0 이하, int 범위 초과 시 예외 발생 후 재입력

- 기능
- 전진 여부 발행
- [x] 0 ~ 9 무작위 발행
- [x] 4 이상일 경우 전진
- [x] 최종 우승자 산출
- [x] 구현한 기능 통합

- 출력
- [x] 라운드 결과 출력
- [x] 우승자 출력

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

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

pobi : --
woni : -
jun : --
2 changes: 1 addition & 1 deletion settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
rootProject.name = "kotlin-racingcar"
rootProject.name = "kotlin-racingcar"
8 changes: 8 additions & 0 deletions src/main/kotlin/racingcar/Application.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package racingcar

import racingcar.controller.RacingController

fun main() {
val racingController = RacingController()
racingController.runRacing()
}
67 changes: 67 additions & 0 deletions src/main/kotlin/racingcar/controller/RacingController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package racingcar.controller

import racingcar.model.Car
import racingcar.service.RacingService
import racingcar.utils.CAR_NAMES_REQUEST_MESSAGE
import racingcar.utils.ROUNDS_RESULT_NOTIFICATION_MESSAGE
import racingcar.utils.ROUND_COUNT_REQUEST_MESSAGE
import racingcar.utils.Validator
import racingcar.view.InputView
import racingcar.view.OutputView

class RacingController(
private val inputView: InputView = InputView(Validator()),
private val outputView: OutputView = OutputView(),
private val racingService: RacingService = RacingService(),

Choose a reason for hiding this comment

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

생성자 주입방식과 기본값 👍

) {
fun runRacing() {
val cars = createCars(readCarNames())
val roundCount = readRoundCount()

runRounds(roundCount, cars)

val winners = getWinners(cars)
printWinners(winners)
}

private fun readCarNames(): List<String> {
outputView.printMessage(CAR_NAMES_REQUEST_MESSAGE)
return inputView.readCarNames()
}

private fun readRoundCount(): Int {
outputView.printMessage(ROUND_COUNT_REQUEST_MESSAGE)
return inputView.readRoundCount()
}

private fun printRoundCountRequestMessage() = outputView.printMessage(ROUNDS_RESULT_NOTIFICATION_MESSAGE)

private fun printRoundResult(cars: List<Car>) = outputView.printRoundResult(cars)

private fun printWinners(winners: List<Car>) = outputView.printWinners(winners)

private fun runRounds(roundCount: Int, cars: List<Car>) {
printRoundCountRequestMessage()
repeat(roundCount) {
runRound(cars)
}
Comment on lines +44 to +46

Choose a reason for hiding this comment

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

Controller의 역할은 무엇인지, 다음 미션의 MVC적용을 하시면서 고민해보시면 좋을거같아요!

}

private fun runRound(cars: List<Car>) {
moveCarsRandomly(cars)
printRoundResult(cars)
}

private fun moveCarsRandomly(cars: List<Car>) {
cars.forEach { car ->
racingService.moveRandomly(car)
}
}

private fun createCars(carNames: List<String>) =

Choose a reason for hiding this comment

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

반환값이 있는경우에는, 반환타입을 명확하게 표기해주는 습관이 좋습니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

반환값이 있는 경우에는 가독성 측면에서 항상 반환 타입을 작성해주는 것이 좋을까요??
어떤 글에서는 '안 쓰는 것이 좋다', '메서드를 보고 알 수 있다면 작성하지 않아도 된다' 등 다양한 의견이 있어 혼동이 생깁니다..!

현업자의 관점에서 보셨을 때 추천하시는 방식이 있으실까요??🙂

Choose a reason for hiding this comment

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

이부분은 스타일의 차이가 있을수 있지만,
저는 간단한 함수라도 모두 작성하는 편입니다.

코드가 많아지다보면, 해당 함수에 대한 확인을 위해 구현부까지 보지않고, 함수명, 파라미터, 리턴값 정도만 확인하는데,
어떤 타입이 반환되는지 직관적으로 알수있는 구조가 아니라면, 가독성 측면에서는 명시적으로 써주는것이 좋을것 같아요!
또한 Int로 생각했는데 실제 값은 double로 반환된다거나하는 실수도 사전에 막을수 있을거 같아요!

Copy link
Member Author

Choose a reason for hiding this comment

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

답변해주신 글을 읽어보니 공감되는 부분이 많습니다!
다들 스타일이 다르겠지만, 가독성 높은 코드라면 대강 함수명, 인자, 반환타입만 보고도 제 3자가 봤을 때 대강 파악할 수 있어야 한다고 생각하는데 그러기 위해서는 작성해주는 것이 좋은 방법이라고 생각됩니다👏

carNames.map { carName ->
racingService.createCar(carName)
}

private fun getWinners(cars: List<Car>): List<Car> = racingService.getWinners(cars)
}
36 changes: 36 additions & 0 deletions src/main/kotlin/racingcar/model/Car.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package racingcar.model

import racingcar.utils.Validator

class Car(
private val name: String,
private var position: Int = 0
) : Comparable<Car> {

init {
val validator = Validator()
validator.checkCarNameLength(name)
}

fun move() = ++position

override fun compareTo(other: Car) = this.position - other.position

override fun equals(other: Any?): Boolean {
if (other is Car) {
return this.position == other.position
}

return false
}

override fun toString() = name

Choose a reason for hiding this comment

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

kotlin에서는 따로 getter함수를 생성해주지 않으셔도 됩니다.
name이 필요한경우, name은 불변변수이기 때문에, 외부에서 바로 접근해도 괜찮을거같아요

한번 공식문서를 참고하시면 좋을거같아요!
https://kotlinlang.org/docs/properties.html

Copy link
Member Author

@tmdgh1592 tmdgh1592 Feb 10, 2023

Choose a reason for hiding this comment

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

오.. 이 부분도 헷갈리던 점이었는데 명쾌한 답변 감사합니다.
굳이 toString() 으로 재정의할 필요 없이 val 키워드로 불변을 보장하니 name에 바로 접근해도 되는군요!
'getter setter 를 지양하라.' 라는 글을 보고 getter는 어디까지 가능한 것인가에 의문이 있었는데 조금 더 고민해봐야겠습니다🥲


override fun hashCode(): Int {
var result = name.hashCode()
result = 31 * result + position
return result
}

fun getPositionAsDash() = "-".repeat(position)

Choose a reason for hiding this comment

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

"-" 문자열같은 매직넘버는 상수로 관리해보면 어떨까요?
관리측면이나, 가독성 측면에서 좋을거 같아요!
+
해당 코드는 View와 관련된 코드라고 볼수도 있을거 같아요!
한번 고민해보셔도 좋을거 같습니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

매직 리터럴, 매직 넘버를 제거하는 것이 좋아보이네요!
중요한 것을 놓치고 있었는데 알려주셔서 감사합니다!
+
다시 살펴보니 비즈니스 로직보다는 View에 보여주기 위한 메서드로 보이네요..!
OutputView에서 처리하도록 리팩토링 해보겠습니다~

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

import racingcar.model.Car
import racingcar.utils.END_RANDOM_MOVEMENT_PROBABILITY
import racingcar.utils.MOVEMENT_PROBABILITY
import racingcar.utils.Random
import racingcar.utils.START_RANDOM_MOVEMENT_PROBABILITY

class RacingService {

fun createCar(carName: String) = Car(carName)

fun moveRandomly(car: Car) {
if (isMove()) {
car.move()
}
}

private fun isMove(): Boolean {
val random = Random.pickInRange(START_RANDOM_MOVEMENT_PROBABILITY, END_RANDOM_MOVEMENT_PROBABILITY)

Choose a reason for hiding this comment

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

외부에서는 랜덤숫자가아닌 isMove만 노출하는군요 👍

Random클래스에 대한 강한 의존이 생겼네요!
Random함수는 테스트하기가 어렵습니다.
의존도를 줄일수 있는 방법을 고민해보시면 좋을거같아요!

if (random < MOVEMENT_PROBABILITY) {
return false
}
return true
}

fun getWinners(cars: List<Car>): List<Car> {
val winnerStandard = cars.max()
return cars.filter { it == winnerStandard }
}
}
11 changes: 11 additions & 0 deletions src/main/kotlin/racingcar/utils/BlankRemover.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package racingcar.utils

object BlankRemover {
fun removeBlank(texts: MutableList<String>) {
for (i in texts.indices) {
texts[i] = removeBlank(texts[i])
}
}

Choose a reason for hiding this comment

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

Blank를 지워주는 클래스를 만드셨네요!

texts.map{ it.trim() }
코틀린의 기본api들로도 충분히 직관적이고 간단하게 구현할수 있을거같아서,
개인적으로 불필요하다고 생각이 들어요!


private fun removeBlank(text: String): String = text.trim()
}
13 changes: 13 additions & 0 deletions src/main/kotlin/racingcar/utils/Dimens.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package racingcar.utils

const val MIN_CAR_NAME_LENGTH = 1
const val MAX_CAR_NAME_LENGTH = 5

const val MIN_ROUND_COUNT = 1
const val MAX_ROUND_COUNT = Int.MAX_VALUE

const val RANDOM_SEED = 10

const val START_RANDOM_MOVEMENT_PROBABILITY = 1
const val END_RANDOM_MOVEMENT_PROBABILITY = 10
const val MOVEMENT_PROBABILITY = 4

Choose a reason for hiding this comment

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

Dimens 파일에 상수를 관리하셨군요

프로그램이 점점 커질수록, 이러한 문자열이 많아져서, 관리되기가 힘들수도 있을거 같아요!
클래스로 분리하거나, 필요한 클래스에서 관리하도록 수정해보면 어떨까요?

필요한 클래스에서 선언하게 된다면,
클래스내 변경사항이 있어도 해당 클래스만 수정할수 있는 장점도 있을거같아요!

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

import kotlin.random.Random

object Random {

private val random = Random(RANDOM_SEED)

fun pickInRange(start: Int, end: Int) = random.nextInt(start, end + 1)

Choose a reason for hiding this comment

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

Suggested change
private val random = Random(RANDOM_SEED)
fun pickInRange(start: Int, end: Int) = random.nextInt(start, end + 1)
fun pickInRange(start: Int, end: Int): Int = (start..end).random()

으로도 간단하게 대체할수 있을거 같아요!

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

// Delimiter
const val CAR_NAME_DELIMITER = ","

// Error
const val CAR_NAME_BOUNDARY_ERROR_MESSAGE = "자동차 이름의 길이는 $MIN_CAR_NAME_LENGTH 이상 $MAX_CAR_NAME_LENGTH 이하로 부탁이요~"
const val DUPLICATED_CAR_NAME_ERROR_MESSAGE = "자동차 이름 중복 빼주세요~"

const val NOT_INTEGER_TYPE_ERROR_MESSAGE = "숫자 형태로 부탁이요~"
const val ROUND_COUNT_BOUNDARY_ERROR_MESSAGE = "라운드 횟수는 $MIN_ROUND_COUNT 이상 $MAX_ROUND_COUNT 이하로 부탁이요~"

// Request Message
const val CAR_NAMES_REQUEST_MESSAGE = "경주할 자동차 이름을 입력하세요(이름은 쉼표(,)를 기준으로 구분)."
const val ROUND_COUNT_REQUEST_MESSAGE = "시도할 횟수는 몇 회인가요?"
const val ROUNDS_RESULT_NOTIFICATION_MESSAGE = "\n실행 결과"
const val WINNER_NOTIFICATION_MESSAGE = "최종 우승자"

Choose a reason for hiding this comment

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

마찬가지로, 관리방법을 고민해보셔도 좋을거같아요!

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

class Validator {

Choose a reason for hiding this comment

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

Validator을 분리하셨군요!
목적에 맞게 검증관련 코드를 모았지만,

클래스이름과 역할이 너무 광범위해지는 단점도 있는거 같아요!
또한 해당 클래스를 사용하는곳에서는 Validator에 대한 강한 의존이 생기게되는 단점도 있을수 있을거같아요!

Copy link
Member Author

Choose a reason for hiding this comment

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

Validator에 대한 의존도가 높아지는 것을 고려하지 못했던 것 같습니다 🥲
단순히 Validator를 사용하는 것보다 각 model에서 init을 통해 객체가 생성될 때 검증하는 방향으로도 생각해보겠습니다~!

fun checkCarNames(names: List<String>): List<String> {
checkDuplicatedCarNames(names)
return names
}

private fun checkDuplicatedCarNames(names: List<String>) {
if (names.toSet().size != names.size) {
throw IllegalArgumentException(DUPLICATED_CAR_NAME_ERROR_MESSAGE)
}
}

fun checkRoundCount(input: String): Int {
val roundCount = input.toIntOrNull()

requireNotNull(roundCount) { NOT_INTEGER_TYPE_ERROR_MESSAGE }

if (roundCount !in MIN_ROUND_COUNT..MAX_ROUND_COUNT) {
throw IllegalArgumentException(ROUND_COUNT_BOUNDARY_ERROR_MESSAGE)
}

return roundCount
}

fun checkCarNameLength(name: String) {
if (name.length !in MIN_CAR_NAME_LENGTH..MAX_CAR_NAME_LENGTH) {
throw IllegalArgumentException(CAR_NAME_BOUNDARY_ERROR_MESSAGE)
}
}
}
17 changes: 17 additions & 0 deletions src/main/kotlin/racingcar/view/InputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package racingcar.view

import racingcar.utils.BlankRemover
import racingcar.utils.CAR_NAME_DELIMITER
import racingcar.utils.Validator

class InputView(private val validator: Validator) {

fun readCarNames(): List<String> {
val names = readln().split(CAR_NAME_DELIMITER).toMutableList()
BlankRemover.removeBlank(names)

return validator.checkCarNames(names)
}

fun readRoundCount() = validator.checkRoundCount(readln())
}
19 changes: 19 additions & 0 deletions src/main/kotlin/racingcar/view/OutputView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package racingcar.view

import racingcar.model.Car
import racingcar.utils.WINNER_NOTIFICATION_MESSAGE

class OutputView {
fun printMessage(message: String) = println(message)

fun printRoundResult(cars: List<Car>) {
cars.forEach { car ->
println("$car : ${car.getPositionAsDash()}")
}
println()
}

fun printWinners(winners: List<Car>) {
println("$WINNER_NOTIFICATION_MESSAGE: ${winners.joinToString(", ")}")
}
}
Loading