From 2b2b066f1d6c0d6d2f74d7b58aac65ed7269e830 Mon Sep 17 00:00:00 2001 From: hand Date: Mon, 14 Nov 2022 21:29:47 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat(Person):=20=EC=BD=94=ED=8B=80=EB=A6=B0?= =?UTF-8?q?=20=ED=95=99=EC=8A=B5=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 5 ++- src/main/kotlin/step1/Person.kt | 7 ++++ src/test/kotlin/step1/PersonTest.kt | 56 +++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/step1/Person.kt create mode 100644 src/test/kotlin/step1/PersonTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index e32e685f0c..ccfb1af4f7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,7 +12,10 @@ repositories { dependencies { testImplementation("org.junit.jupiter", "junit-jupiter", "5.8.2") - testImplementation("org.assertj", "assertj-core", "3.22.0") + + // https://github.com/assertj/assertj/issues/2357 + testImplementation("org.assertj", "assertj-core", "3.20.2") + testImplementation("io.kotest", "kotest-runner-junit5", "5.2.3") } diff --git a/src/main/kotlin/step1/Person.kt b/src/main/kotlin/step1/Person.kt new file mode 100644 index 0000000000..b3a5d35123 --- /dev/null +++ b/src/main/kotlin/step1/Person.kt @@ -0,0 +1,7 @@ +package step1 + +data class Person( + val name: String, + val age: Int? = null, + var nickname: String? = null +) diff --git a/src/test/kotlin/step1/PersonTest.kt b/src/test/kotlin/step1/PersonTest.kt new file mode 100644 index 0000000000..3048b7b09c --- /dev/null +++ b/src/test/kotlin/step1/PersonTest.kt @@ -0,0 +1,56 @@ +package step1 + +import io.kotest.matchers.shouldBe +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertAll + +internal class PersonTest { + + @Test + fun constructor() { + val person = Person( + name = "손민성", + age = 30, + nickname = "손너잘" + ) + + person.name shouldBe ("손민성") + + person.nickname = "너잘손" + person.nickname shouldBe ("너잘손") + } + + @Test + fun `named arguments`() { + val people = listOf( + Person("손민성", 30, "손너잘"), + Person("손민성", 30, nickname = "손너잘"), + Person(name = "손민성", age = 30, nickname = "손너잘") + ) + + assertThat(people).allSatisfy { + it.name shouldBe "손민성" + it.age shouldBe 30 + it.nickname shouldBe "손너잘" + } + } + + @Test + fun `default arguments`() { + val person = Person("손민성") + + assertAll( + { person.name shouldBe "손민성" }, + { person.age shouldBe null }, + { person.nickname shouldBe null } + ) + } + + @Test + fun `data classes`() { + val person1 = Person("손민성", 30, "손너잘") + val person2 = Person("손민성", 30, "손너잘") + assertThat(person1).isEqualTo(person2) + } +} From 8aa3b97302a78821c74d1056f89a9877938f6d9a Mon Sep 17 00:00:00 2001 From: hand Date: Tue, 27 Dec 2022 01:14:55 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20gradle=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index ccfb1af4f7..643a13fc9b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + plugins { kotlin("jvm") version "1.6.21" id("org.jlleitschuh.gradle.ktlint") version "10.2.1" @@ -17,6 +19,7 @@ dependencies { testImplementation("org.assertj", "assertj-core", "3.20.2") testImplementation("io.kotest", "kotest-runner-junit5", "5.2.3") + implementation(kotlin("stdlib-jdk8")) } tasks { @@ -33,3 +36,11 @@ tasks { verbose.set(true) } } +val compileKotlin: KotlinCompile by tasks +compileKotlin.kotlinOptions { + jvmTarget = "1.8" +} +val compileTestKotlin: KotlinCompile by tasks +compileTestKotlin.kotlinOptions { + jvmTarget = "1.8" +} From 73e2d5c8c50e0e3c9e992926de3f60d7d2db9cff Mon Sep 17 00:00:00 2001 From: hand Date: Tue, 27 Dec 2022 01:15:26 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat(calculator):=20Number=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=9E=91=EC=84=B1.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/step2/calculator/vo/CalculatorInput.kt | 4 ++++ src/main/kotlin/step2/calculator/vo/Number.kt | 5 +++++ 2 files changed, 9 insertions(+) create mode 100644 src/main/kotlin/step2/calculator/vo/CalculatorInput.kt create mode 100644 src/main/kotlin/step2/calculator/vo/Number.kt diff --git a/src/main/kotlin/step2/calculator/vo/CalculatorInput.kt b/src/main/kotlin/step2/calculator/vo/CalculatorInput.kt new file mode 100644 index 0000000000..f4ce0612c7 --- /dev/null +++ b/src/main/kotlin/step2/calculator/vo/CalculatorInput.kt @@ -0,0 +1,4 @@ +package step2.calculator.vo + +interface CalculatorInput { +} diff --git a/src/main/kotlin/step2/calculator/vo/Number.kt b/src/main/kotlin/step2/calculator/vo/Number.kt new file mode 100644 index 0000000000..e8a9e8d8ed --- /dev/null +++ b/src/main/kotlin/step2/calculator/vo/Number.kt @@ -0,0 +1,5 @@ +package step2.calculator.vo + +data class Number( + val value: Int +) : CalculatorInput From f2ce01f042b8291568b1f45f73ad91420ce51431 Mon Sep 17 00:00:00 2001 From: hand Date: Tue, 27 Dec 2022 01:45:45 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat(calculator):=20Operation=20enum=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/step2/calculator/vo/Operation.kt | 17 ++++++ .../step2/calculator/vo/OperationTest.kt | 57 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/main/kotlin/step2/calculator/vo/Operation.kt create mode 100644 src/test/kotlin/step2/calculator/vo/OperationTest.kt diff --git a/src/main/kotlin/step2/calculator/vo/Operation.kt b/src/main/kotlin/step2/calculator/vo/Operation.kt new file mode 100644 index 0000000000..e6e2a0038c --- /dev/null +++ b/src/main/kotlin/step2/calculator/vo/Operation.kt @@ -0,0 +1,17 @@ +package step2.calculator.vo + +import java.util.function.BiFunction + +enum class Operation( + private val value: String, + private val operation: BiFunction +) : CalculatorInput { + PLUS("+", { op1, op2 -> Number(op1.value + op2.value) }), + MINUS("-", { op1, op2 -> Number(op1.value - op2.value) }), + DIVIDE("/", { op1, op2 -> Number(op1.value / op2.value) }), + MULTI("*", { op1, op2 -> Number(op1.value * op2.value) }); + + fun calculate(op1: Number, op2: Number): Number { + return this.operation.apply(op1, op2) + } +} diff --git a/src/test/kotlin/step2/calculator/vo/OperationTest.kt b/src/test/kotlin/step2/calculator/vo/OperationTest.kt new file mode 100644 index 0000000000..b86cca8f4f --- /dev/null +++ b/src/test/kotlin/step2/calculator/vo/OperationTest.kt @@ -0,0 +1,57 @@ +package step2.calculator.vo + +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.data.forAll +import io.kotest.data.headers +import io.kotest.data.row +import io.kotest.data.table +import io.kotest.matchers.shouldBe +import step2.calculator.vo.Operation.* + +class OperationTest : DescribeSpec({ + table( + headers("op1", "op2"), + row(Number(10), Number(5)), + row(Number(1), Number(5)), + row(Number(3), Number(3)), + row(Number(0), Number(1)) + ).forAll { op1, op2 -> + describe("숫자 ${op1.value}, ${op2.value} 가 주어지고") { + context("+ 연산자로 계산하면") { + val actual = PLUS.calculate(op1, op2) + val expected = op1.value + op2.value + + it("$expected (이)가 반환된다.") { + actual shouldBe Number(expected) + } + } + + context("- 연산자로 계산하면") { + val actual = MINUS.calculate(op1, op2) + val expected = op1.value - op2.value + + it("$expected (이)가 반환된다.") { + actual shouldBe Number(expected) + } + } + + context("/ 연산자로 계산하면") { + val actual = DIVIDE.calculate(op1, op2) + val expected = op1.value / op2.value + + it("$expected (이)가 반환된다.") { + actual shouldBe Number(expected) + } + } + + context("* 연산자로 계산하면") { + val actual = MULTI.calculate(op1, op2) + val expected = op1.value * op2.value + + it("$expected (이)가 반환된다.") { + actual shouldBe Number(expected) + } + } + } + } +}) From 8f3e43ef7897e1244ae90492012b6a6f156e593f Mon Sep 17 00:00:00 2001 From: hand Date: Tue, 27 Dec 2022 03:08:04 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feat(calculator):=20CalculatorInput=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EB=B3=80=ED=99=98=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../step2/calculator/vo/CalculatorInput.kt | 30 ++++++++++ .../kotlin/step2/calculator/vo/Operation.kt | 8 +++ .../calculator/vo/CalculatorInputKtTest.kt | 58 +++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 src/test/kotlin/step2/calculator/vo/CalculatorInputKtTest.kt diff --git a/src/main/kotlin/step2/calculator/vo/CalculatorInput.kt b/src/main/kotlin/step2/calculator/vo/CalculatorInput.kt index f4ce0612c7..f5b6933ee9 100644 --- a/src/main/kotlin/step2/calculator/vo/CalculatorInput.kt +++ b/src/main/kotlin/step2/calculator/vo/CalculatorInput.kt @@ -1,4 +1,34 @@ package step2.calculator.vo interface CalculatorInput { + companion object +} + +fun CalculatorInput.Companion.from(input: String): CalculatorInput { + return when { + input.isTypeOf(Number::class.java) -> Number(input.toInt()) + input.isTypeOf(Operation::class.java) -> Operation.from(input) + else -> throw IllegalArgumentException("알 수 없는 입력값.") + } +} + + +fun String.isTypeOf(type: Class): Boolean { + return when { + type.isAssignableFrom(Number::class.java) -> { + runCatching { + Number(this.toInt()) + }.isSuccess + } + + type.isAssignableFrom(Operation::class.java) -> { + kotlin.runCatching { + Operation.from(this) + }.isSuccess + } + + else -> { + false + } + } } diff --git a/src/main/kotlin/step2/calculator/vo/Operation.kt b/src/main/kotlin/step2/calculator/vo/Operation.kt index e6e2a0038c..99fcc46810 100644 --- a/src/main/kotlin/step2/calculator/vo/Operation.kt +++ b/src/main/kotlin/step2/calculator/vo/Operation.kt @@ -14,4 +14,12 @@ enum class Operation( fun calculate(op1: Number, op2: Number): Number { return this.operation.apply(op1, op2) } + + companion object { + fun from(input: String): Operation { + return values().asSequence() + .filter { it.value == input } + .first() + } + } } diff --git a/src/test/kotlin/step2/calculator/vo/CalculatorInputKtTest.kt b/src/test/kotlin/step2/calculator/vo/CalculatorInputKtTest.kt new file mode 100644 index 0000000000..a25f691d37 --- /dev/null +++ b/src/test/kotlin/step2/calculator/vo/CalculatorInputKtTest.kt @@ -0,0 +1,58 @@ +package step2.calculator.vo + +import io.kotest.assertions.throwables.shouldThrowAny +import io.kotest.core.spec.style.DescribeSpec +import io.kotest.data.forAll +import io.kotest.data.headers +import io.kotest.data.row +import io.kotest.data.table +import io.kotest.inspectors.forAll +import io.kotest.matchers.shouldBe + +class CalculatorInputKtTest : DescribeSpec({ + + table( + headers("input", "type"), + row("+", Operation::class.java), + row("-", Operation::class.java), + row("*", Operation::class.java), + row("/", Operation::class.java), + row("1234", Number::class.java), + row("4334", Number::class.java), + row("124", Number::class.java), + row("12235", Number::class.java), + ).forAll { input, expected -> + describe("CalculatorInput에 입력값 $input 을 넣고") { + val calculatorInput = CalculatorInput.from(input) + + context("$expected 타입을 반환하는지 확인하면") { + val actual = calculatorInput.javaClass.isAssignableFrom(expected) + + it("성공한다.") { + actual shouldBe true + } + } + } + } + + listOf( + "ㅂ", + "-4f3", + "*d", + "/2", + "123sd4", + "4_334", + "12d4", + "122a35", + ).forAll { input -> + describe("CalculatorInput에 입력값 $input 을 넣고") { + context("성공을 확인하면") { + it("실패한다.") { + shouldThrowAny { + CalculatorInput.from(input) + } + } + } + } + } +}) From 1e06675bfe74dbab649293ae9da505e0557ee7f4 Mon Sep 17 00:00:00 2001 From: hand Date: Tue, 27 Dec 2022 03:17:16 +0900 Subject: [PATCH 6/9] =?UTF-8?q?feat(calculator):=20Calculator=20=EC=A0=95?= =?UTF-8?q?=EC=A0=81=20=ED=8C=A9=ED=84=B0=EB=A6=AC=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B5=AC=ED=98=84.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/step2/calculator/Calculator.kt | 21 +++++++++++++++++++ .../kotlin/step2/calculator/CalculatorTest.kt | 15 +++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/main/kotlin/step2/calculator/Calculator.kt create mode 100644 src/test/kotlin/step2/calculator/CalculatorTest.kt diff --git a/src/main/kotlin/step2/calculator/Calculator.kt b/src/main/kotlin/step2/calculator/Calculator.kt new file mode 100644 index 0000000000..11f3267193 --- /dev/null +++ b/src/main/kotlin/step2/calculator/Calculator.kt @@ -0,0 +1,21 @@ +package step2.calculator + +import step2.calculator.vo.CalculatorInput +import step2.calculator.vo.from + +class Calculator( + private val inputs: List +) { + companion object +} + +fun Calculator.Companion.from(input: String): Calculator { + return Calculator(extractInputs(input)); +} + +private fun extractInputs(input: String): List { + return input.split(" ").asSequence() + .filter { it.isNotBlank() } + .map { CalculatorInput.from(it) } + .toList(); +} diff --git a/src/test/kotlin/step2/calculator/CalculatorTest.kt b/src/test/kotlin/step2/calculator/CalculatorTest.kt new file mode 100644 index 0000000000..c00b411544 --- /dev/null +++ b/src/test/kotlin/step2/calculator/CalculatorTest.kt @@ -0,0 +1,15 @@ +package step2.calculator + +import io.kotest.assertions.throwables.shouldNotThrowAny +import io.kotest.assertions.throwables.shouldThrowAny +import io.kotest.core.spec.style.StringSpec + +class CalculatorTest : StringSpec({ + "올바른 인풋에 대해서 오류 없이 계산기 생성이 가능하다." { + shouldNotThrowAny { Calculator.from("3 + 2 * 4") } + } + + "올바르지 않은 인풋에 대해서 오류 없이 계산기 생성이 불가능하다." { + shouldThrowAny { Calculator.from("3+ 2 * 4") } + } +}) From faa4dddaacd27a6fcde29e59f82459b97fdab687 Mon Sep 17 00:00:00 2001 From: hand Date: Tue, 27 Dec 2022 04:34:48 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feat(calculator):=20Inputs=20validation=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/step2/calculator/Inputs.kt | 75 +++++++++++++++++++ .../kotlin/step2/calculator/InputsTest.kt | 59 +++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 src/main/kotlin/step2/calculator/Inputs.kt create mode 100644 src/test/kotlin/step2/calculator/InputsTest.kt diff --git a/src/main/kotlin/step2/calculator/Inputs.kt b/src/main/kotlin/step2/calculator/Inputs.kt new file mode 100644 index 0000000000..8515bd8222 --- /dev/null +++ b/src/main/kotlin/step2/calculator/Inputs.kt @@ -0,0 +1,75 @@ +package step2.calculator + +import step2.calculator.vo.CalculatorInput +import step2.calculator.vo.Number +import step2.calculator.vo.Operation + +class Inputs( + private val numbers: List, + private val operations: List +) { + companion object +} + +@Suppress("UNCHECKED_CAST") +fun Inputs.Companion.from(values: List): Inputs { + values.validateFormat() + + val groupBy = values.groupBy { it.getType() } + + return Inputs( + groupBy[Number::class.java] as List, + groupBy[Operation::class.java] as List + ) +} + +private fun List.validateFormat() { + validateNumberPosition() + validateOperationOption() + validateOperandCount() + validateInputLength() +} + +private fun List.validateNumberPosition() { + for (i: Int in 0 until this.size step (2)) { + if (this[i] !is Number) { + throw IllegalArgumentException("인풋 포멧 오류") + } + } +} + +private fun List.validateOperationOption() { + for (i: Int in 1 until this.size step (2)) { + if (this[i] !is Operation) { + throw IllegalArgumentException("인풋 포멧 오류") + } + } +} + +private fun List.validateOperandCount() { + val groupBy = this.groupingBy { + it.getType() + }.eachCount() + + if (groupBy[Number::class.java] != groupBy[Operation::class.java]?.plus(1)) { + throw IllegalArgumentException("피연산자 오류") + } +} + +private fun List.validateInputLength() { + if (this.size < 3) { + throw IllegalArgumentException("인풋 포멧 오류") + } +} + +private fun CalculatorInput.getType(): Class { + if (this is Number) { + return Number::class.java + } + + if (this is Operation) { + return Operation::class.java + } + + throw IllegalArgumentException("잘못된 타입") +} diff --git a/src/test/kotlin/step2/calculator/InputsTest.kt b/src/test/kotlin/step2/calculator/InputsTest.kt new file mode 100644 index 0000000000..a0ab39e932 --- /dev/null +++ b/src/test/kotlin/step2/calculator/InputsTest.kt @@ -0,0 +1,59 @@ +package step2.calculator + +import io.kotest.assertions.throwables.shouldNotThrowAny +import io.kotest.assertions.throwables.shouldThrowAny +import io.kotest.core.spec.style.StringSpec +import step2.calculator.vo.Number +import step2.calculator.vo.Operation + +class InputsTest : StringSpec({ + "정확한 input 포멧으로 Inputs를 생성한다." { + shouldNotThrowAny { + Inputs.from( + listOf( + Number(1), + Operation.from("+"), + Number(2), + Operation.from("-"), + Number(3) + ) + ) + } + } + + "input 포멧이 부정확 하면 exception. (피연산자)" { + shouldThrowAny { + Inputs.from( + listOf( + Number(1), + Operation.from("+"), + Number(2), + Operation.from("-") + ) + ) + } + } + + "input 포멧이 부정확 하면 exception.(길이)" { + shouldThrowAny { + Inputs.from( + listOf( + Number(1), + Operation.from("+") + ) + ) + } + } + + "input 포멧이 부정확 하면 exception.(순서)" { + shouldThrowAny { + Inputs.from( + listOf( + Number(1), + Operation.from("+"), + Operation.from("-") + ) + ) + } + } +}) From eff2fecdb001c9eea4847911377d3196b81c7b8f Mon Sep 17 00:00:00 2001 From: hand Date: Tue, 27 Dec 2022 04:47:17 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feat(calculator):=20Inputs=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/step2/calculator/Inputs.kt | 31 +++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/step2/calculator/Inputs.kt b/src/main/kotlin/step2/calculator/Inputs.kt index 8515bd8222..e93afb3cd0 100644 --- a/src/main/kotlin/step2/calculator/Inputs.kt +++ b/src/main/kotlin/step2/calculator/Inputs.kt @@ -6,8 +6,37 @@ import step2.calculator.vo.Operation class Inputs( private val numbers: List, - private val operations: List + private val operations: List, ) { + private var numbersPosition: Int = 0 + private var operationsPosition: Int = 0 + + val hasNextNumberPair: Boolean + get() { + return numbers.size < numbersPosition + } + + val nextNumberP: Number + get() { + val result = numbers[numbersPosition] + numbersPosition += 1 + + return result + } + + val hasNextOperation: Boolean + get() { + return operations.size < operationsPosition + } + + val nextOperation: Operation + get() { + val result = operations[operationsPosition] + operationsPosition += 1 + + return result + } + companion object } From ea45268efd299118b159854042011b2cb94d1584 Mon Sep 17 00:00:00 2001 From: hand Date: Tue, 27 Dec 2022 04:53:19 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feat(calculator):=20=EA=B3=84=EC=82=B0?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/kotlin/step2/calculator/Calculator.kt | 18 ++++++++++++++++-- src/main/kotlin/step2/calculator/Inputs.kt | 8 ++++---- .../kotlin/step2/calculator/CalculatorTest.kt | 13 +++++++++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/step2/calculator/Calculator.kt b/src/main/kotlin/step2/calculator/Calculator.kt index 11f3267193..4edd201cc0 100644 --- a/src/main/kotlin/step2/calculator/Calculator.kt +++ b/src/main/kotlin/step2/calculator/Calculator.kt @@ -1,16 +1,30 @@ package step2.calculator import step2.calculator.vo.CalculatorInput +import step2.calculator.vo.Number import step2.calculator.vo.from class Calculator( - private val inputs: List + private val inputs: Inputs ) { + fun calculate(): Number { + var result = inputs.nextNumber; + + while(inputs.hasNextNumber && inputs.hasNextOperation) { + val operation = inputs.nextOperation + val operand = inputs.nextNumber + + result = operation.calculate(result, operand) + } + + return result + } + companion object } fun Calculator.Companion.from(input: String): Calculator { - return Calculator(extractInputs(input)); + return Calculator(Inputs.from(extractInputs(input))) } private fun extractInputs(input: String): List { diff --git a/src/main/kotlin/step2/calculator/Inputs.kt b/src/main/kotlin/step2/calculator/Inputs.kt index e93afb3cd0..7ba7dbd213 100644 --- a/src/main/kotlin/step2/calculator/Inputs.kt +++ b/src/main/kotlin/step2/calculator/Inputs.kt @@ -11,12 +11,12 @@ class Inputs( private var numbersPosition: Int = 0 private var operationsPosition: Int = 0 - val hasNextNumberPair: Boolean + val hasNextNumber: Boolean get() { - return numbers.size < numbersPosition + return numbers.size > numbersPosition } - val nextNumberP: Number + val nextNumber: Number get() { val result = numbers[numbersPosition] numbersPosition += 1 @@ -26,7 +26,7 @@ class Inputs( val hasNextOperation: Boolean get() { - return operations.size < operationsPosition + return operations.size > operationsPosition } val nextOperation: Operation diff --git a/src/test/kotlin/step2/calculator/CalculatorTest.kt b/src/test/kotlin/step2/calculator/CalculatorTest.kt index c00b411544..183e1997d8 100644 --- a/src/test/kotlin/step2/calculator/CalculatorTest.kt +++ b/src/test/kotlin/step2/calculator/CalculatorTest.kt @@ -1,8 +1,11 @@ package step2.calculator +import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldNotThrowAny import io.kotest.assertions.throwables.shouldThrowAny import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import step2.calculator.vo.Number class CalculatorTest : StringSpec({ "올바른 인풋에 대해서 오류 없이 계산기 생성이 가능하다." { @@ -12,4 +15,14 @@ class CalculatorTest : StringSpec({ "올바르지 않은 인풋에 대해서 오류 없이 계산기 생성이 불가능하다." { shouldThrowAny { Calculator.from("3+ 2 * 4") } } + + "계산결과가 출력된다." { + assertSoftly { + Calculator.from("3 + 2 * 4").calculate() shouldBe Number(20) + Calculator.from("3 + 2 * 4 + 1").calculate() shouldBe Number(21) + Calculator.from("3 + 2 * 4 / 5").calculate() shouldBe Number(4) + Calculator.from("3 + 2 * 4 * 5").calculate() shouldBe Number(100) + } + + } })