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

[둘리] 코틀린 자동차 경주 제출합니다. #50

Merged
merged 76 commits into from
Feb 10, 2023

Conversation

hyemdooly
Copy link

안녕하세요, 리뷰어님! 바쁘신 와중에 제 코드를 리뷰해주셔서 감사합니다. :)

@hyemdooly hyemdooly changed the title [둘리] 자동차 경주 제출합니다. [둘리] 코틀린 자동차 경주 제출합니다. Feb 9, 2023
Copy link

@BeokBeok BeokBeok left a comment

Choose a reason for hiding this comment

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

자동차 경주 구현 미션하시느라 고생하셨습니다. 👍
고민해볼만한 의견들을 코맨트로 작성하였으니, 충분히 고민해보시고 도전해보세요. 💪

Comment on lines 8 to 9
private val outputView = OutputView()
private val inputView = InputView()
Copy link

Choose a reason for hiding this comment

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

멤버 변수를 활용해주셨네요. 👍
멤버 변수는 클래스 내부에서 언제든지 접근해서 사용할 수 있는 장점이 있지만, 함수 호출 시 멤버 변수의 값에 따라 달라지는 단점이 있습니다.
가급적이면 멤버 변수를 사용하지 않고 코드를 개선해보면 어떨까요?

ref. https://dev.to/adammc331/the-imposters-guide-to-dependency-injection-1eak

val cars = executeInputCarNames()
val tryNumber = executeInputTryNumber()
outputView.outputResults()
for (i in 1..tryNumber) {
Copy link

Choose a reason for hiding this comment

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

Suggested change
for (i in 1..tryNumber) {
repeat(tryNumber) {

repeat을 활용해보면 어떨까요?

outputView.outputResults()
for (i in 1..tryNumber) {
tryMove(cars)
println()
Copy link

Choose a reason for hiding this comment

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

println은 출력과 관련된 함수입니다.
OutputView에서 관리해보면 어떨까요?

}

private fun getInputTryNumber(number: String): Int {
return try {
Copy link

Choose a reason for hiding this comment

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

코틀린에서는 runCatching이라는 것을 지원합니다.
runCatching에 대해서 학습해보시고 적용해보면 어떨까요? (이하 관련 내용 동일)


class RandomGenerator {
fun getRandomNumber(): Int {
return (0..9).random()
Copy link

Choose a reason for hiding this comment

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

0과 9는 각각 어떤 것을 의미할까요?
숫자 0과 9와 같은 것은 "매직리터럴"이라고 합니다.
"매직리터럴"에 대해서 학습해보시고, 어떻게 개선해보면 좋을지 고민해보면 어떨까요? (이하 관련 내용 동일)

}

private fun mappingCars(input: String): List<Car> = input.split(",").mapIndexed { _, name -> Car(name.trim()) }
fun getCarInfo(position: Int): Pair<String, Int> = cars[position].getInfo()
Copy link

Choose a reason for hiding this comment

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

Pair을 활용해주셨네요. 👍
Pair는 두 개의 값을 편하게 wrapping할 수 있다는 장점이 있지만, first와 second가 어떤 것을 의미하는지 알기가 어렵습니다.
Pair 대신 data class를 활용해보면 어떨까요?

Comment on lines 3 to 5
class Constants {

companion object {
Copy link

Choose a reason for hiding this comment

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

Suggested change
class Constants {
companion object {
object Constants {

클래스 내 companion object만 존재한다면, object를 활용해보면 어떨까요?

Comment on lines 20 to 23
fun findWinners(): List<String> {
val equalCars = cars.groupBy({ it.getInfo().second }, { it.getInfo().first })
return equalCars[equalCars.keys.max()]?.toList() ?: listOf()
}
Copy link

Choose a reason for hiding this comment

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

우승자를 찾는 함수를 잘 구현해주셨네요. 👍
하지만 Cars 클래스는 자동차의 리스트를 관리하는 책임을 가지고 있는데, 우승자를 찾는 함수로 인하여 우승자를 관리하는 책임까지 가지게 됩니다.
우승자를 찾는 책임을 갖는 클래스를 만들어보면 어떨까요?

Comment on lines 15 to 17
fun move(random: Int) {
if (random >= 4) position++
}
Copy link

Choose a reason for hiding this comment

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

랜덤값을 파라미터로 전달 👍

Comment on lines 16 to 23
@Test
fun moveTest() {
val car = Car("dool", 0)
car.move(3)
assertThat(car.getInfo().second).isEqualTo(0)
car.move(4)
assertThat(car.getInfo().second).isEqualTo(1)
}
Copy link

Choose a reason for hiding this comment

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

하나의 테스트 함수에 두 가지 케이스를 테스트하고 있네요.
만약 요구사항이 3 이상일 경우 전진하는 것으로 변경된다면, 테스트 코드가 반쪽은 통과하고 반쪽은 통과하지 못하게 돼요.
4 이상일 경우와 4 미만인 경우 케이스를 분리해서 테스트 코드를 작성해보면 어떨까요?

@hyemdooly
Copy link
Author

피드백 감사합니다!! 수정하는 중 궁금증이 생겼습니다.

  1. 피드백 주신대로 CarInfo 클래스를 만들어 수정했습니다. 그런데 CarInfo에 정보를 담아 리턴해주는 것보다 cars의 private와 car의 name, position의 private를 풀어서 외부에서 get을 할 수 있게 하는 방법이 생각났습니다. 이 방법이 더 좋은 코드일까요?

  2. 사용자가 잘못된 값을 입력했을 때, 제대로 된 값을 입력할 때까지 반복하고 싶은데요. runCatching를 사용하는 것으로 수정하면서 null처리를 하기 위해 execute-()함수들의 코드가 길어졌습니다. 제가 보기에는 클린하지 않아보이는데 다른 방법이 있을까요? 아예 InputView에서 예외처리를 하는 것이 좋을까요?

Copy link

@BeokBeok BeokBeok left a comment

Choose a reason for hiding this comment

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

피드백 반영하시느라 고생하셨습니다. 👍
추가로 고민해볼만한 의견들을 코맨트로 작성하였으니, 충분히 고민해보시고 도전해보세요. 💪
다음 미션을 진행하시면서, 추가로 작성된 코맨트들도 반영해주세요. 🙏

Comment on lines +5 to +6
class CarsHelper {
companion object {

Choose a reason for hiding this comment

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

@@ -0,0 +1,3 @@
package model

data class CarInfo(val name: String, val position: Int)

Choose a reason for hiding this comment

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

Car 클래스를 보시면, 캡슐화를 하고 있는 프로퍼티가 동일합니다.
CarInfo보다는 Car 클래스를 활용하도록 코드를 개선해보면 어떨까요?

@BeokBeok BeokBeok merged commit 599b70b into woowacourse:hyemdooly Feb 10, 2023
@BeokBeok
Copy link

피드백 주신대로 CarInfo 클래스를 만들어 수정했습니다. 그런데 CarInfo에 정보를 담아 리턴해주는 것보다 cars의 private와 car의 name, position의 private를 풀어서 외부에서 get을 할 수 있게 하는 방법이 생각났습니다. 이 방법이 더 좋은 코드일까요?

A. 코맨트에 작성했듯이, Car 클래스를 활용해보면 좋을 것 같아요!
하지만, position과 같은 경우에는 내부에서는 변경이 가능해야 하며 외부에서는 변경이 안되도록 해야 하므로, private를 풀어버린다면 외부에서 값을 변경할 수 있게 됩니다.
getter를 함수로 구현해주셔도 좋지만, backing property를 활용할 수 있어요.

ref. https://kotlinlang.org/docs/coding-conventions.html#names-for-backing-properties

@BeokBeok
Copy link

사용자가 잘못된 값을 입력했을 때, 제대로 된 값을 입력할 때까지 반복하고 싶은데요. runCatching를 사용하는 것으로 수정하면서 null처리를 하기 위해 execute-()함수들의 코드가 길어졌습니다. 제가 보기에는 클린하지 않아보이는데 다른 방법이 있을까요? 아예 InputView에서 예외처리를 하는 것이 좋을까요?

A. exception은 논리 오류를 뜻하며, 논리 오류는 개발자에게 버그를 알리는 방법입니다.
try-catch 문에서 에러가 발생하게 되면 catch문에 도달하게 되고, 여기서 재귀호출로 인하여 반복해주고 있는 것으로 봤는데요.
만약 지금과 같은 코드를 작성하게 되면, 프로그램이 어떤 이유로 인해서 예외가 발생했는지 알 수 없게 됩니다.
입력과 관련된 책임은 InputView가 가지고 있습니다.
제대로 된 값을 입력할 때까지 반복을 하고 싶다면, RacingGame이 아닌 InputView에서 반복을 하도록 개선해보면 어떨까요?

ref. https://elizarov.medium.com/kotlin-and-exceptions-8062f589d07

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants