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

Step2 | 문자열 계산기 #1773

Open
wants to merge 8 commits into
base: slowth-kim
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
# kotlin-racingcar
# kotlin-racingcar

## 문자열 계산기 테스트 케이스 목록

기본 연산 테스트

- [x] 덧셈: "2 + 3" = 5
- [x] 뺄셈: "5 - 3" = 2
- [x] 곱셈: "4 * 3" = 12
- [x] 나눗셈: "8 / 2" = 4

예외 처리 테스트

- [x] null 입력 처리
- [x] 빈 문자열 입력 처리
- [x] 잘못된 연산자 입력 처리 ("2 @ 3")

복합 연산 테스트

- [x] 두 개의 연산: "2 + 3 * 4" = 20
- [x] 세 개의 연산: "2 + 3 * 4 / 2" = 10
- [x] 동일 우선순위 연산: "1 + 2 + 3" = 6
Comment on lines +5 to +22
Copy link
Member

Choose a reason for hiding this comment

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

요구사항 정리 👍

54 changes: 54 additions & 0 deletions src/main/kotlin/step2/StringCalculator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package step2

class StringCalculator {
fun calculate(expression: String): Int {
validateInput(expression)

val tokens = expression.trim().split(" ")

var result = tokens[0].toInt()

var i = 1
while (i < tokens.size - 1) {
val operator = tokens[i]
val number = tokens[i + 1].toInt()

result = performOperation(result, operator, number)
i += 2
}

return result
Comment on lines +11 to +20
Copy link
Member

Choose a reason for hiding this comment

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

while 대신 for 문을 사용해도 충분해보이는걸요? 😉

}

private fun validateInput(expression: String) {
if (expression.isBlank()) {
throw IllegalArgumentException("입력값이 null이거나 빈 문자열입니다.")
}

val tokens = expression.trim().split(" ")
if (tokens.size < 3 || tokens.size % 2 == 0) {
throw IllegalArgumentException("올바르지 않은 수식 형식입니다.")
}

tokens.filterIndexed { index, _ -> index % 2 == 1 }
.forEach { operator ->
if (operator !in validOperators) {
throw IllegalArgumentException("사칙연산 기호가 아닙니다.")
}
}
}

private fun performOperation(a: Int, operator: String, b: Int): Int {
return when (operator) {
"+" -> a + b
"-" -> a - b
"*" -> a * b
"/" -> if (b != 0) a / b else throw IllegalArgumentException("0으로 나눌 수 없습니다.")
else -> throw IllegalArgumentException("지원하지 않는 연산자입니다.")
}
}

companion object {
private val validOperators = setOf("+", "-", "*", "/")
}
Comment on lines +41 to +53
Copy link
Member

Choose a reason for hiding this comment

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

0으로 나누기 불가하다는 예외처리까지 👍
사칙연산 기호를 정적으로 관리하고 싶다는 니즈가 느껴지는데요,
이를 enum class 를 활용하여 따로 관리해보는 건 어떨까요?

}
67 changes: 67 additions & 0 deletions src/test/kotlin/step2/StringCalculatorTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package step2

import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.style.FunSpec
import io.kotest.data.forAll
import io.kotest.data.row
import io.kotest.matchers.shouldBe


class StringCalculatorTest : FunSpec({
val calculator = StringCalculator()

context("기본 사칙 연산") {
test("덧셈 연산을 수행한다") {
calculator.calculate("2 + 3") shouldBe 5
}

test("뺄셈 연산을 수행한다") {
calculator.calculate("5 - 3") shouldBe 2
}

test("곱셈 연산을 수행한다") {
calculator.calculate("4 * 3") shouldBe 12
}

test("나눗셈 연산을 수행한다") {
calculator.calculate("8 / 2") shouldBe 4
}
}


context("예외 상황 처리") {
test("null 또는 빈 문자열 입력시 예외가 발생한다") {
shouldThrow<IllegalArgumentException> {
calculator.calculate("")
}.message shouldBe "입력값이 null이거나 빈 문자열입니다."

shouldThrow<IllegalArgumentException> {
calculator.calculate(" ")
}.message shouldBe "입력값이 null이거나 빈 문자열입니다."
}

test("잘못된 연산자 입력시 예외가 발생한다") {
forAll(
row("2 @ 3"),
row("4 # 5"),
row("6 $ 7")
) { expression ->
shouldThrow<IllegalArgumentException> {
calculator.calculate(expression)
}.message shouldBe "사칙연산 기호가 아닙니다."
}
}
}

context("복합 연산") {
test("여러 개의 연산을 순서대로 처리한다") {
forAll(
row("2 + 3 * 4 / 2", 10),
row("1 + 2 + 3", 6),
row("10 - 2 * 3", 24)
) { expression, expected ->
calculator.calculate(expression) shouldBe expected
}
}
}
})