-
Notifications
You must be signed in to change notification settings - Fork 202
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
[코드 리뷰용 PR입니다!] - 재구현 스터디 #234
base: main
Are you sure you want to change the base?
Changes from all commits
a099e67
74afdaf
d8174b1
af95c62
24d2595
0255b4a
35894ba
8251585
5a0f00f
db617d1
93dec51
162bc8e
11396a7
ac5f83f
b76936d
c84fe0d
7f30ea8
456d445
530df2b
2a59198
0daa19f
f0e4f33
bc2a4f1
b842fcf
55cc32e
31e8e73
e80ab00
5040a91
abea5d7
b5c9769
914e198
ab3bc60
31d9c82
acf4a7a
dca30bd
76d82bf
b7d47bb
d4dba45
9058246
a13d7cd
b9f5f74
361f0b9
d98ef9a
a984551
5ad80d8
d4b857f
8178e1d
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 |
---|---|---|
@@ -0,0 +1,65 @@ | ||
## 기능 목록(한 눈에 보기) | ||
|
||
``` | ||
• 게임 시작 전 | ||
◦ 경주할 자동차 이름을 입력받는다. (1) - InputView#getUserInput() | ||
▫︎ 자동차 이름은 쉼표로 구분한다. - Util#seperatorNameByComma() | ||
▫︎ 자동차 이름은 5자 이하여야 하며, 넘어갈 시 애러를 발생시킨다. (3) - Validator#validateLength() | ||
▫︎ 자동자 이름에 중복이 있다면 에러를 발생시킨다. (3) - Validator#validateUnique() | ||
▫︎ 자동차 이름에 null값이 들어오면 에러를 발생시킨다. (3) - Validator#validateNotNull() | ||
◦ 게임 횟수를 입력받는다. (2) - InputView#getUserInputCount() | ||
▫︎ 입력이 숫자가 아닐 시 이러를 발생시킨다. (3) - Validator#validateInteger() | ||
▫︎ 입력의 범위를 벗어날 시 에러를 발생시킨다. (3) - Validator#validateRange() | ||
|
||
• 게임 중 | ||
◦ 0부터 9까지의 숫자를 랜덤하게 구한다. - MainController#getRandomNumber() | ||
▫︎ 구한 숫자가 4이상의 숫자인지 확인한다. - MainController#checkMovable() | ||
◦ 4이상의 숫자일 시 자동차를 전진한다. - Car#moveForward() | ||
▫︎ 자동차 전진은 '-'으로 표기한다. - OutputView#printDistance() | ||
▫︎ 단 첫번째 출력시에는 자동차 이름도 같이 표기한다. - OutputView#printCarName() | ||
(ex. pobi : -) | ||
◦ 게임 횟수만큼 반복한다. - MainController#playOneCycle() | ||
|
||
• 게임 종료 | ||
◦ 최종 우승자 리스트를 가져온다. - Cars#getWinners() | ||
◦ 최종 우승자를 출력한다. (4) - OutputView#printWinners() | ||
▫︎ 게임 우승자는 여러명이 나올 수도 있으며, 여러명일 경우에는 쉼표로 구분하여 출력한다. | ||
|
||
(1) 랜덤값 추출은 camp.nextstep.edu.missionutils.Randoms.pickNumberInRange()을 이용한다. | ||
(2) 사용자 입력은 camp.nextstep.edu.missionutils.Console.readLine()을 이용한다. | ||
(3) 예외처리는 IllegalArgumentException을 발생시켜 애플리케이션을 종료시킨다. | ||
(4) 게임 종료 시 System.exit()를 호출하지 않는다. | ||
``` | ||
|
||
<br><br> | ||
|
||
## 기능 목록 체크리스트 | ||
|
||
- 게임 시작 전 | ||
- [x] 게임 시작 문구를 출력한다. | ||
- [x] 자동차 경주 이름을 입력받는다. | ||
- [x] 자동차 이름들을 구분한다. | ||
- [x] 자동차 이름의 유효성을 검사한다. | ||
- [x] 게임 횟수를 입력받는다. | ||
- [x] 게임 횟수의 유효성을 검사한다. | ||
|
||
<br> | ||
|
||
- 게임 시작 | ||
- [x] 0부터 9까지의 숫자를 정한다. | ||
- [x] 만약 정한 숫자가 4보다 크면 한칸 움직인다. | ||
- [x] 처음에 입력 받는 횟수만큼 반복한다. | ||
|
||
<br> | ||
|
||
- 게임 종료 | ||
- [x] 최종 우승자를 출력한다. | ||
- [x] 여러 명일 경우에는 쉼표로 구분하여 출력한다. | ||
|
||
<br><br> | ||
|
||
## 프로그래밍 요구 사항 | ||
|
||
- [ ] indent(인덴트, 들여쓰기)는 depth를 3이 넘지 않도록 구현한다. | ||
- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라. | ||
- [ ] JUnit 5와 AssertJ를 이용하여 기능 목록이 정상적으로 작동하는지 테스트 코드를 작성하여 확인한다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,12 @@ | ||
package racingcar | ||
|
||
import racingcar.controller.MainController | ||
import racingcar.view.InputView | ||
import racingcar.view.OutputView | ||
|
||
fun main() { | ||
// TODO: 프로그램 구현 | ||
val outputView = OutputView() | ||
val inputView = InputView() | ||
val controller = MainController(inputView, outputView) | ||
controller.run() | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package racingcar.controller | ||
|
||
import racingcar.view.InputView | ||
import racingcar.view.OutputView | ||
import racingcar.model.Cars | ||
import racingcar.util.Util.getRandomNumber | ||
import racingcar.util.Util.isMovable | ||
|
||
class MainController(private val inputView: InputView, private val outputView: OutputView) { | ||
private val cars = Cars() | ||
private var gameCount = 0 | ||
|
||
fun run() { | ||
gameInit() | ||
gameStart() | ||
gameEnd() | ||
} | ||
|
||
private fun gameStart() { | ||
repeat(gameCount) { | ||
playOneCycle() | ||
} | ||
} | ||
|
||
private fun gameInit() { | ||
inputCarNames() | ||
inputGameCount() | ||
} | ||
|
||
private fun playOneCycle() { | ||
cars.carNames.forEach { car -> | ||
val isMovable = isMovable(getRandomNumber()) | ||
car.isMove(isMovable) | ||
outputView.printCarDistance(car) | ||
} | ||
println() | ||
} | ||
|
||
private fun gameEnd() { | ||
val winners = cars.getWinners() | ||
outputView.printWinners(winners) | ||
} | ||
|
||
private fun inputCarNames() { | ||
outputView.printInputCarNameMessage() | ||
val carNames = inputView.getInputCarNames() | ||
cars.addAllList(carNames) | ||
} | ||
|
||
private fun inputGameCount() { | ||
outputView.printInputCountMessage() | ||
gameCount = inputView.getInputCount() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package racingcar.model | ||
|
||
class Car(private val _name: String) { | ||
|
||
val name: String | ||
get() = _name | ||
|
||
private var _distance = 0 | ||
val distance: Int | ||
get() = _distance | ||
|
||
fun isMove(movable: Boolean) { | ||
if (movable) _distance++ | ||
} | ||
|
||
fun isWinner(maxValue: Int): Boolean = _distance == maxValue | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package racingcar.model | ||
|
||
class Cars { | ||
private var _carNames = mutableListOf<Car>() | ||
val carNames: List<Car> | ||
get() = _carNames | ||
|
||
fun addAllList(carNames: List<String>) = _carNames.addAll(carNames.map { Car(it) }) | ||
|
||
fun getMaxDistance(): Int = _carNames.maxOf { it.distance } | ||
|
||
fun getWinners(): List<String> { | ||
val maxDistance = getMaxDistance() | ||
return _carNames.filter { it.isWinner(maxDistance) }.map { it.name } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package racingcar.util | ||
|
||
object Constants { | ||
const val MAX_NAME_LENGTH = 5 | ||
const val START_NUMBER = 0 | ||
const val END_NUMBER = 9 | ||
const val MIN_COUNT_NUMBER = 1 | ||
const val MOVABLE_NUMBER = 4 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package racingcar.util | ||
|
||
enum class ExceptionMessage(private val message: String) { | ||
INVALID_LENGTH("자동차의 이름이 5보다 깁니다."), | ||
INVALID_UNIQUE_NAME("중복된 자동차의 이름이 존재합니다."), | ||
INVALID_CAR_NAME("자동차 이름에 널값이 존재합니다."), | ||
INVALID_INTEGER("사용자의 입력이 정수가 아닙니다."), | ||
INVALID_RANGE("사용자의 입력이 유효한 범위가 아닙니다."); | ||
|
||
fun getMessage(): String = message | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package racingcar.util | ||
|
||
import camp.nextstep.edu.missionutils.Randoms | ||
|
||
object Util { | ||
fun separateNameByComma(carNames: String): List<String> = carNames.split(",") | ||
|
||
fun getRandomNumber(): Int = Randoms.pickNumberInRange(Constants.START_NUMBER, Constants.END_NUMBER) | ||
|
||
fun isMovable(number: Int): Boolean = number >= Constants.MOVABLE_NUMBER | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package racingcar.util | ||
|
||
import racingcar.util.Constants.MAX_NAME_LENGTH | ||
import racingcar.util.Constants.MIN_COUNT_NUMBER | ||
import java.lang.IllegalArgumentException | ||
|
||
object Validator { | ||
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. 1주차와 마찬가지로 전체적으로 |
||
fun validateLength(numberList: List<String>) { | ||
numberList.forEach { | ||
if (it.length > MAX_NAME_LENGTH) throw IllegalArgumentException(ExceptionMessage.INVALID_LENGTH.getMessage()) | ||
} | ||
} | ||
|
||
fun validateUnique(numberList: List<String>) { | ||
val validation = numberList.toSet() | ||
if (validation.size != numberList.size) throw IllegalArgumentException(ExceptionMessage.INVALID_UNIQUE_NAME.getMessage()) | ||
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. 중요한 것은 아닐 수도 있는데, 2주차 피드백에 보면 변수명에 자료형은 사용하지 않는다라는 내용이 있습니다. 저도 잘 지키지는 못하는 부분중에 하나인데 혹시 다음번에도 리팩토링을 더 하실 계획이 있다면 도움이 되지 않을까 싶어서 남깁니다:) |
||
} | ||
|
||
fun validateNotNull(numberList: List<String>) { | ||
numberList.forEach { | ||
if (it.trim().isEmpty()) throw IllegalArgumentException(ExceptionMessage.INVALID_CAR_NAME.getMessage()) | ||
} | ||
} | ||
|
||
fun validateInteger(number: String) { | ||
number.toIntOrNull() ?: throw IllegalArgumentException(ExceptionMessage.INVALID_INTEGER.getMessage()) | ||
} | ||
|
||
fun validateRange(number: String) { | ||
val validation = number.toInt() | ||
if (validation !in MIN_COUNT_NUMBER..Int.MAX_VALUE) throw IllegalArgumentException(ExceptionMessage.INVALID_RANGE.getMessage()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package racingcar.view | ||
|
||
import camp.nextstep.edu.missionutils.Console | ||
import racingcar.util.Util.separateNameByComma | ||
import racingcar.util.Validator.validateInteger | ||
import racingcar.util.Validator.validateLength | ||
import racingcar.util.Validator.validateNotNull | ||
import racingcar.util.Validator.validateRange | ||
import racingcar.util.Validator.validateUnique | ||
|
||
class InputView { | ||
private fun getUserInput(): String = Console.readLine() | ||
|
||
fun getInputCarNames(): List<String> { | ||
val input = separateNameByComma(getUserInput()) | ||
validateLength(input) | ||
validateUnique(input) | ||
validateNotNull(input) | ||
return input | ||
} | ||
Comment on lines
+14
to
+20
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. Validator.kt내의 함수명들을 직관적으로 잘 지어주셔서 만약 위에 파일을 읽지 않았더라도 getInputCarNames에서 어떤 것들을 검증하는지 이해하기 편할 것 같네요! |
||
|
||
fun getInputCount(): Int { | ||
val input = getUserInput() | ||
validateInteger(input) | ||
validateRange(input) | ||
return input.toInt() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package racingcar.view | ||
|
||
import racingcar.model.Car | ||
|
||
class OutputView { | ||
fun printInputCarNameMessage() { | ||
println("경주할 자동차 이름을 입력하세요.(이름은 쉼표(,) 기준으로 구분)") | ||
} | ||
|
||
fun printInputCountMessage() { | ||
println("시도할 횟수는 몇 회인가요?") | ||
} | ||
|
||
fun printCarDistance(car: Car) { | ||
println("${car.name} : ${"-".repeat(car.distance)}") | ||
} | ||
|
||
fun printWinners(winners: List<String>) { | ||
val output = winners.joinToString(", ") | ||
println("최종 우승자 : $output") | ||
} | ||
} |
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.
이 부분 체크리스트도 확인해보시는 것도 좋을 것 같아요!
마지막 제출 전 확인 필수!