面试题:
“用任意一种面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。”
以下的程序是利用计算机的方式去思考。看起来并没有什么大的问题,也能得到正确的结果,但是这种思维却使我们的程序只能满足实现当前的需求,不符合易维护、易扩展、易复用,达不到高质量代码的需求!
/**
* @create on 2020/4/23 22:50
* @description 计算器控制台程序(两个数的运算)
* @author mrdonkey
*/
class Program {
companion object {
@JvmStatic
fun main(vararg args: String) {
val sc = Scanner(System.`in`)
println("请输入数字A")
val strNumberA = sc.nextLine()
println("请输入运算符")
val operate = sc.nextLine()
println("请输入数字B")
val strNumberB = sc.nextLine()
val strResult: String = when (operate) {
"+" -> {
strNumberA.toDouble().plus(strNumberB.toDouble()).toString()
}
"-" -> {
strNumberA.toDouble().minus(strNumberB.toDouble()).toString()
}
"*" -> {
strNumberA.toDouble().times(strNumberB.toDouble()).toString()
}
"/" -> {
if (strNumberB != "0")
strNumberA.toDouble().div(strNumberB.toDouble()).toString()
else
"除数不能为0"
}
else -> {
"不支持该运算"
}
}
println("结果是:$strResult")
}
}
}
测试结果:
请输入数字A
123
请输入运算符
*
请输入数字B
3
结果是:369.0
将业务逻辑(计算操作)与界面逻辑(输入结果)分开,让它们 的耦合度降低
Operate操作类
/**
* @create on 2020/4/23 23:11
* @description 把计算的操作类抽出来
* @author mrdonkey
*/
class Operate {
companion object {
@JvmStatic
fun getResult(numberA: Double, numberB: Double, operate: String): Double {
return when (operate) {
"+" -> {
numberA.plus(numberB)
}
"-" -> {
numberA.minus(numberB)
}
"*" -> {
numberA.times(numberB)
}
"/" -> {
numberA.div(numberB)
}
else -> {
0.toDouble()
}
}
}
}
}
封装后的程序
/**
* @create on 2020/4/23 23:09
* @description 利用封装来实现 将页面逻辑与业务逻辑分开
* @author mrdonkey
*/
class PackageProgram {
companion object {
@JvmStatic
fun main(vararg args: String) {
try {
val sc = Scanner(System.`in`)
println("请输入数字A")
val numberA = sc.nextLine().toDouble()
println("请选择运算符(+、-、*、/):")
val operate = sc.nextLine()
println("请输入数字B")
val numberB = sc.nextLine().toDouble()
println("输出结果是:${Operate.getResult(numberA, numberB,operate)}")
} catch (e: Exception) {
println("你输入有错:${e.message}")
}
}
}
}
输出同上
如果要再之上的程序中,添加一种sqrt开根的运算,需要在Operate类的switch加多一个分支。问题是加一个开根运算,却需要让加减乘除的运算都得来参与编译,如果把加法运算不小心改成减法,这将是多糟糕。应该把单独的操作运算分离出来,提取父类(继承),修改其中一个一个操作不影响其他几个,每一个操作都最自己不同的实现(多态)。
Operation类
/**
* @create on 2020/4/23 23:26
* @description 利用继承和多态来减少耦合
* @author mrdonkey
*/
abstract class Operation {
abstract fun getResult(numberA: Double, numberB: Double): Double
}
/**
* @create on 2020/4/23 23:28
* @description TODO
* @author 加法
*/
class OperationPlus : Operation() {
override fun getResult(numberA: Double, numberB: Double) = numberA.plus(numberB)
}
OperationMinus
/**
* @create on 2020/4/23 23:28
* @description TODO
* @author 加法
*/
/**
* @create on 2020/4/23 23:29
* @description 减法
* @author mrdonkey
*/
class OperationMinus : Operation() {
override fun getResult(numberA: Double, numberB: Double) = numberA.minus(numberB)
}
OperationTimes
/**
* @create on 2020/4/23 23:30
* @description 乘法
* @author mrdonkey
*/
class OperationTimes : Operation() {
override fun getResult(numberA: Double, numberB: Double) = numberA.times(numberB)
}
OperationDiv
/**
* @create on 2020/4/23 23:30
* @description 除法
* @author mrdonkey
*/
class OperationDiv : Operation() {
override fun getResult(numberA: Double, numberB: Double) = numberA.div(numberB)
}
提取了父类,并且提供一个getResult的抽象方法由具体子类来实现不同的运算操作,现在我们的修个任何一个运算方法,都不会影响其他的运算的代码了。但是我如何让计算器知道我希望用哪个运算呢?
用一个单独的类来决定实例化谁、用哪个运算!这就是简单工厂的职责。
OperationFactory
/**
* @create on 2020/4/23 23:33
* @description 操作的工厂,让它知道该实例化哪个操作
* @author mrdonkey
*/
class OperationFactory {
companion object {
@JvmStatic
fun creatOperate(operate: String): Operation? = when (operate) {
"+" -> {
OperationPlus()
}
"-" -> {
OperationMinus()
}
"*" -> {
OperationTimes()
}
"/" -> {
OperationDiv()
}
else -> {
null
}
}
}
}
Client测试
/**
* @create on 2020/4/23 23:37
* @description 客户端测试
* @author mrdonkey
*/
class Client {
companion object {
@JvmStatic
fun main(vararg args: String) {
try {
val sc = Scanner(System.`in`)
println("请输入数字A")
val numberA = sc.nextLine().toDouble()
println("请选择运算符(+、-、*、/):")
val operate = sc.nextLine()
println("请输入数字B")
val numberB = sc.nextLine().toDouble()
val operation = OperationFactory.creatOperate(operate)
println("输出结果是:${operation?.getResult(numberA, numberB)}")
} catch (e: Exception) {
println("你输入有错:${e.message}")
}
}
}
}
输出同上
这样子,你只需要输入相应的运算符号,工厂就实例化对应的运算对象,通过多态返回父类方式实现了计算器的结果。如果以后需要增加各种复杂运算,比如平方根、立方根等,只需要增加对应的子类运算和给工厂添加相应的switch分支即可。