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

[코드 리뷰용 PR입니다!] - 재구현 스터디 #234

Open
wants to merge 47 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
a099e67
docs: MVC적용 기능 목록 작성
lh99j Nov 2, 2023
74afdaf
feat: 게임시작 문구 출력 기능 구현
lh99j Nov 2, 2023
d8174b1
feat: 사용자의 입력 가져오는 기능 구현
lh99j Nov 2, 2023
af95c62
feat: 자동차 이름 컴마로 구분하여 리스트로 반환하는 기능 구현
lh99j Nov 2, 2023
24d2595
test: 컴마로 구분하여 리스트 반환 기능 검증
lh99j Nov 2, 2023
0255b4a
feat: 자동차 이름길이 검증 기능 구현
lh99j Nov 2, 2023
35894ba
test: 자동차 이름 길이 검증 테스트
lh99j Nov 2, 2023
8251585
feat: 사용자 입력 중복 검증 기능 구현
lh99j Nov 2, 2023
5a0f00f
test: 사용자 입력 중복 검증 테스트
lh99j Nov 2, 2023
db617d1
feat: 사용자 입력 널값 검증 기능 구현
lh99j Nov 2, 2023
93dec51
test: 사용자 입력 널값 검증 테스트
lh99j Nov 2, 2023
162bc8e
docs: 진행상황 업데이트
lh99j Nov 2, 2023
11396a7
feat: 횟수 입력 메세지 출력 기능 구현
lh99j Nov 2, 2023
ac5f83f
feat: 사용자 입력 정수인지 검증 기능 구현
lh99j Nov 2, 2023
b76936d
test: 사용자 입력이 정수인지 검증 기능 테스트
lh99j Nov 2, 2023
c84fe0d
feat: 사용자 입력의 범위 검증 기능 구현
lh99j Nov 2, 2023
7f30ea8
test: 사용자 입력의 범위 검증 기능 테스트
lh99j Nov 2, 2023
456d445
docs: 기능 목록 및 진행상황 업데이트
lh99j Nov 2, 2023
530df2b
feat: 랜덤으로 숫자 가져오는 기능 구현
lh99j Nov 2, 2023
2a59198
feat: 자동차 전진 기능 구현
lh99j Nov 2, 2023
0daa19f
test: 자동차 전진 기능 검증
lh99j Nov 2, 2023
f0e4f33
feat: Cars 클래스 생성
lh99j Nov 2, 2023
bc2a4f1
feat: 4이상이면 true 반환 기능 구현
lh99j Nov 2, 2023
b842fcf
feat: 게임 로직 구현
lh99j Nov 2, 2023
55cc32e
test: isMovable 함수 테스트
lh99j Nov 2, 2023
31e8e73
fix: 테스트 값 수정
lh99j Nov 2, 2023
e80ab00
docs: 체크리스트 업데이트
lh99j Nov 2, 2023
5040a91
feat: 게임 로직 구현
lh99j Nov 2, 2023
abea5d7
fix: print() 위치로 인한 버그 수정
lh99j Nov 2, 2023
b5c9769
feat: 자동차 이동거리의 최대값 반환 기능 구현
lh99j Nov 2, 2023
914e198
test: 자동차 이동거리 최대값 반환 검증
lh99j Nov 2, 2023
ab3bc60
feat: 우승자인지 확인하는 기능 검증
lh99j Nov 2, 2023
31d9c82
test: 우승자인지 확인 기능 검증
lh99j Nov 2, 2023
acf4a7a
feat: 우승자 반환 기능 구현
lh99j Nov 2, 2023
dca30bd
test: 우승자 반환 기능 검증
lh99j Nov 2, 2023
76d82bf
feat: 우승자 출력 기능 구현
lh99j Nov 2, 2023
b7d47bb
docs: 기능 목록 업데이트
lh99j Nov 2, 2023
d4dba45
refactor: 게임 로직 분리
lh99j Nov 2, 2023
9058246
fix: 게임 로직 호출 수정
lh99j Nov 2, 2023
a13d7cd
fix: model 패키지 위치 수정
lh99j Nov 2, 2023
b9f5f74
fix: 최종 우승자 출력문 수정
lh99j Nov 2, 2023
361f0b9
refactor: ExceptionMessage enum class 생성
lh99j Nov 2, 2023
d98ef9a
refactor: Constants 클래스로 불변값 관리
lh99j Nov 2, 2023
a984551
refactor: 유틸 함수 Util class로 이동 및 외부 호출 없는 함수 private로 처리
lh99j Nov 2, 2023
5ad80d8
refactor: 외부 호출 없는 함수는 private로 만들기
lh99j Nov 2, 2023
d4b857f
style: 안쓰는 import 구문 제거
lh99j Nov 2, 2023
8178e1d
refactor: gameInit 함수 분리
lh99j Nov 2, 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
65 changes: 65 additions & 0 deletions docs/README.md
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>

## 프로그래밍 요구 사항

Choose a reason for hiding this comment

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

이 부분 체크리스트도 확인해보시는 것도 좋을 것 같아요!
마지막 제출 전 확인 필수!


- [ ] indent(인덴트, 들여쓰기)는 depth를 3이 넘지 않도록 구현한다.
- [ ] 함수(또는 메서드)가 한 가지 일만 하도록 최대한 작게 만들어라.
- [ ] JUnit 5와 AssertJ를 이용하여 기능 목록이 정상적으로 작동하는지 테스트 코드를 작성하여 확인한다.
9 changes: 8 additions & 1 deletion src/main/kotlin/racingcar/Application.kt
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()
}
54 changes: 54 additions & 0 deletions src/main/kotlin/racingcar/controller/MainController.kt
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()
}
}
17 changes: 17 additions & 0 deletions src/main/kotlin/racingcar/model/Car.kt
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
}
16 changes: 16 additions & 0 deletions src/main/kotlin/racingcar/model/Cars.kt
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 }
}
}
9 changes: 9 additions & 0 deletions src/main/kotlin/racingcar/util/Constants.kt
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
}
11 changes: 11 additions & 0 deletions src/main/kotlin/racingcar/util/ExceptionMessage.kt
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
}
11 changes: 11 additions & 0 deletions src/main/kotlin/racingcar/util/Util.kt
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
}
33 changes: 33 additions & 0 deletions src/main/kotlin/racingcar/util/Validator.kt
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 {

Choose a reason for hiding this comment

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

1주차와 마찬가지로 전체적으로 requre() 문을 사용하는 것이 더 좋아보여요!

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())

Choose a reason for hiding this comment

The 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())
}
}
28 changes: 28 additions & 0 deletions src/main/kotlin/racingcar/view/InputView.kt
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

Choose a reason for hiding this comment

The 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()
}
}
22 changes: 22 additions & 0 deletions src/main/kotlin/racingcar/view/OutputView.kt
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")
}
}
Loading