面試受挫
面試題:
“用任意一種面嚮對象語言實現一個計算器控制檯程序,要求輸入兩個數和運算符號,得到結果。”
初學者的代碼
以下的程序是利用計算機的方式去思考。看起來並沒有什麼大的問題,也能得到正確的結果,但是這種思維卻使我們的程序只能滿足實現當前的需求,不符合易維護、易擴展、易複用,達不到高質量代碼的需求!
/**
* @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
}
- OperationPlus
/**
* @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的抽象方法由具體子類來實現不同的運算操作,現在我們的修個任何一個運算方法,都不會影響其他的運算的代碼了。但是我如何讓計算器知道我希望用哪個運算呢?
簡單工廠模式的應用
用一個單獨的類來決定實例化誰、用哪個運算!這就是簡單工廠的職責。
-
UML類圖結構
-
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分支即可。