Kotlin 朱涛-4 实战 计算器 单元测试 大数加法
目录
04 | 实战:构建一个四则运算计算器
计算器 1.0
- 选择菜单 File -> New ->
Project
- 或者 File -> New ->
Project from Version Control
导入 GitHub 工程
基础代码
fun main() {
while(true) {
println("请输入标准的算式,并且按回车; \n" +
"比如:1 + 1,注意符合与数字之间要有空格。\n" +
"输入exit,退出程序。")
var input = readLine()
if (input == null) continue
if (input == "exit") exitProcess(0)
var inputList = input.split(" ")
var result = calculate(inputList)
if (result == null) {
println("输入格式不对")
} else {
println("$input = $result")
}
}
}
private fun calculate(inputList: List<String>): Int? {
if (inputList.size != 3) return null
var left = inputList.get(0).toInt()
var operation = inputList.get(1)
var right = inputList.get(2).toInt()
when(operation) {
"+" -> return left + right
"-" -> return left - right
"*" -> return left * right
"/" -> return left / right
else -> return null
}
}
代码优化
- ①:使用 Kotlin 的原始字符串
- ②:使用 Elvis 表达式
- ③:程序中所有的 var 都应该尽可能改为 val
- ④:Kotlin 统一了数组和集合的元素访问操作,哪种方便哪种来
- ⑤:可以将 return 放到 when 表达式的前面
- ⑤:结合
枚举或密封类
使用 when 表达式,可以省略 else 分支
val help = """
--------------------------------------
使用说明:
1. 输入 1 + 1,按回车,即可使用计算器;
2. 注意:数字与符号之间要有空格;
3. 想要退出程序,请输入:exit
--------------------------------------""".trimIndent()
fun main() {
while (true) {
println(help)
val input = readLine() ?: continue
if (input == "exit") exitProcess(0)
val inputList = input.split(" ")
val result = calculate(inputList)
if (result == null) {
println("输入格式不对")
continue
} else {
println("$input = $result")
}
}
}
private fun calculate(inputList: List<String>): Int? {
if (inputList.size != 3) return null
val left = inputList[0].toInt()
val operation = Operation.valueOf(inputList[1]) // 这里其实还有一些问题
val right = inputList[2].toInt()
return when (operation) {
Operation.ADD -> left + right
Operation.MINUS -> left - right
Operation.MULTI -> left * right
Operation.DIVI -> left / right
}
}
enum class Operation(val value: String) {
ADD("+"),
MINUS("-"),
MULTI("*"),
DIVI("/")
}
计算器 2.0
优化方向:
- 融入面向对象的思想:将程序封装到一个类中,并尽量让每个函数的功能划分清楚、简单
- 兼容输入格式:兼容不同的输入格式,不管数字和符号之间有没有空格,都能成功执行
import kotlin.system.exitProcess
class CalculatorV2 {
private val exit = "exit"
private val help = """
--------------------------------------
使用说明:
1. 输入 1 + 1,按回车,即可使用计算器;
2. 注意:数字与符号之间要有空格;
3. 想要退出程序,请输入:exit
--------------------------------------""".trimIndent()
fun start() {
while (true) {
println(help)
val input = readLine() ?: continue
val result = calculate(input)
if (result == null) {
println("输入格式不对")
continue
} else {
println("$input = $result")
}
}
}
fun calculate(input: String): String? {
if (shouldExit(input)) exitProcess(0) // 封装是否需要退出的逻辑到一个方法中
val exp = parseExpression(input) ?: return null // 封装解析逻辑到一个方法中
val left = exp.left
val operator = exp.operator
val right = exp.right
return when (operator) {
Operation.ADD -> addString(left, right)
Operation.MINUS -> minusString(left, right)
Operation.MULTI -> multiString(left, right)
Operation.DIVI -> diviString(left, right)
}
}
private fun shouldExit(input: String): Boolean {
return input == exit
}
private fun parseExpression(input: String): Expression? {
val operation = parseOperator(input) ?: return null
val list = input.split(operation.value)
if (list.size != 2) return null
return Expression(list[0].trim(), operation, list[1].trim())
}
private fun parseOperator(input: String): Operation? {
Operation.values().forEach { // 枚举的遍历
if (input.contains(it.value)) {
return it
}
}
return null
}
private fun addString(left: String, right: String): String {
val result = left.toInt() + right.toInt()
return result.toString()
}
private fun minusString(left: String, right: String): String {
val result = left.toInt() - right.toInt()
return result.toString()
}
private fun multiString(left: String, right: String): String {
val result = left.toInt() * right.toInt()
return result.toString()
}
private fun diviString(left: String, right: String): String {
val result = left.toInt() / right.toInt()
return result.toString()
}
}
enum class Operation(val value: String) {
ADD("+"),
MINUS("-"),
MULTI("*"),
DIVI("/")
}
data class Expression(
val left: String,
val operator: Operation,
val right: String
)
fun main() {
val calculator = CalculatorV2()
calculator.start()
}
计算器 3.0
优化方向:
- 增加单元测试:以类为单元,对类中的方法进行一一测试
- 支持大数的加法:对特别大的数进行兼容
单元测试
要在 Kotlin 中使用单元测试,需要添加 Kotlin 官方提供的依赖:
testImplementation 'org.jetbrains.kotlin:kotlin-test'
单元测试的代码,一般放在工程的 test
目录下。
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class TestCalculatorV3 {
@Test
fun testCalculate() {
val calculator = CalculatorV2()
assertEquals("2", calculator.calculate("1+1"))
assertEquals("2333333333333333", calculator.calculate("2333333333333332+1"))
}
}
- 使用注解
@Test
修饰用来做测试的方法 - 执行断言
assertEquals
,如果两个参数相等,单元测试就会成功,否则就会失败
在实际的开发工作当中,单元测试是需要尽量覆盖所有情况的。比如上面这个计算器,它的单元测试用例数量会达到几百上千个。
单元测试不仅可以验证新开发的功能,同时还可以用于保证旧的功能不受影响。在实际开发工作中,我们很容易因为对功能 A 的改动,导致功能 B 出问题。然后往往由于时间限制,测试人员只测试了功能 A,忽略了功能 B,最终导致线上故障带来经济损失。
而借助单元测试,在每一次的开发工作完成以后,我们就统一跑一遍所有的单元测试,只要单元测试通过了,我们就能保证新的功能没问题,而旧的功能也没问题。
大数加法
大数加法是面试中的一道高频题,它的解题思路很简单,就是通过模拟我们手写加法竖式的方法,从个位、十位、百位、千位,一直累加,超过 10 的时候,我们需要进位
。
private fun addString(leftNum: String, rightNum: String): String {
val result = StringBuilder()
var leftIndex = leftNum.length - 1 // 默认指向数字的个位(字符串最后一位字符)
var rightIndex = rightNum.length - 1
var carry = 0 // 存储每一位计算结果的进位
while (leftIndex >= 0 || rightIndex >= 0) {
// 取每一位上的数字,如果超出最大位,则返回 0
val leftVal = if (leftIndex >= 0) leftNum[leftIndex].digitToInt() else 0
val rightVal = if (rightIndex >= 0) rightNum[rightIndex].digitToInt() else 0
val sum = leftVal + rightVal + carry // 计算当前位的值
carry = sum / 10 // 计算当前位的进位
result.append(sum % 10) // 计算当前位的结果
leftIndex-- // 从低位一直到高位,直到遍历完它们所有的数字位
rightIndex--
}
if (carry != 0) { // 遍历完之后,判断最高位是否还有进位
result.append(carry)
}
return result.reverse().toString() // 翻转回来
}
2018-06-11
本文来自博客园,作者:白乾涛,转载请注明原文链接:https://www.cnblogs.com/baiqiantao/p/9168094.html