1.代码无错就是优?-简单工厂模式 (大话设计模式Kotlin版)

内容大纲

    • 面试受挫
    • 初学者的代码
    • 利用封装的实现
    • 利用继承和多态的方式实现松耦合
    • 简单工厂模式的应用

面试受挫

面试题:

“用任意一种面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。”

初学者的代码

以下的程序是利用计算机的方式去思考。看起来并没有什么大的问题,也能得到正确的结果,但是这种思维却使我们的程序只能满足实现当前的需求,不符合易维护、易扩展、易复用,达不到高质量代码的需求!

/**
 * @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类图结构
1.代码无错就是优?-简单工厂模式 (大话设计模式Kotlin版)_第1张图片

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分支即可。

你可能感兴趣的:(设计模式)