10.考题抄错会做也白搭-模板方法模式 (大话设计模式Kotlin版)

内容大纲

    • 面试题
    • 重复=易错+难改
    • 提炼后的代码
    • 模板方法
      • 模板方法的特点

面试题

老师布置了一次金庸小说的考卷试题,要求甲乙学生抄题并给出对应答案,请用编程语言实现它。
10.考题抄错会做也白搭-模板方法模式 (大话设计模式Kotlin版)_第1张图片

重复=易错+难改

下面实现的UML图
10.考题抄错会做也白搭-模板方法模式 (大话设计模式Kotlin版)_第2张图片

这不简单嘛,分别创建两个类:甲抄的试卷、乙抄的试卷
甲抄的试卷

/**
 * @create on 2020/4/24 14:29
 * @description 学生甲抄的试卷
 * @author mrdonkey
 */
class TestPaperA {

    /**
     * 试题1
     */
    fun testQuestion1() {
        println("杨过得到,后来给了郭靖,练成倚天剑、屠龙刀的玄铁可能是[] a.球磨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维")
        println("答案:b")
    }

    /**
     * 试题2
     */
    fun testQuestion2() {
        println("杨过、程英、陆无双铲除了情花,造成[] a.使这种植物不再害人 b.使一种珍稀物种灭绝 c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化")
        println("答案:a")
    }

    /**
     * 试题1
     */
    fun testQuestion3() {
        println("蓝凤凰致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们开什么药?[] a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.让他们喝大量的生牛奶 e.以上全不对")
        println("答案:c")
    }
}

乙抄的试卷

/**
 * @create on 2020/4/24 14:29
 * @description 学生乙抄的试卷
 * @author mrdonkey
 */
class TestPaperB {

    /**
     * 试题1
     */
    fun testQuestion1() {
        println("杨过得到,后来给了郭靖,练成倚天剑、屠龙刀的玄铁可能是[] a.球磨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维")
        println("答案:d")
    }

    /**
     * 试题2
     */
    fun testQuestion2() {
        println("杨过、程英、陆无双铲除了情花,造成[] a.使这种植物不再害人 b.使一种珍稀物种灭绝 c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化")
        println("答案:b")
    }

    /**
     * 试题1
     */
    fun testQuestion3() {
        println("蓝凤凰致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们开什么药?[] a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.让他们喝大量的生牛奶 e.以上全不对")
        println("答案:a")
    }
}

客户端类

/**
 * @create on 2020/4/24 14:40
 * @description 客户端测试
 * @author mrdonkey
 */
class Client {
    companion object {
        @JvmStatic
        fun main(vararg args: String) {
            println("甲抄的试卷")
            val paperA = TestPaperA()
            paperA.testQuestion1()
            paperA.testQuestion2()
            paperA.testQuestion3()
            println("乙抄的试卷")
            val paperB = TestPaperB()
            paperB.testQuestion1()
            paperB.testQuestion2()
            paperB.testQuestion3()
        }
    }
}

测试结果

甲抄的试卷
杨过得到,后来给了郭靖,练成倚天剑、屠龙刀的玄铁可能是[] a.球磨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维
答案:b
杨过、程英、陆无双铲除了情花,造成[] a.使这种植物不再害人 b.使一种珍稀物种灭绝 c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化
答案:a
蓝凤凰致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们开什么药?[] a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.让他们喝大量的生牛奶 e.以上全不对
答案:c
乙抄的试卷
杨过得到,后来给了郭靖,练成倚天剑、屠龙刀的玄铁可能是[] a.球磨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维
答案:d
杨过、程英、陆无双铲除了情花,造成[] a.使这种植物不再害人 b.使一种珍稀物种灭绝 c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化
答案:b
蓝凤凰致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们开什么药?[] a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.让他们喝大量的生牛奶 e.以上全不对
答案:a

以上的甲乙学生的代码存在非常多的相似逻辑,除了答案不同,没什么不一样,这样重复的代码维护两份之后改起来就很难改并且难以维护。例如,老师需要增加题目,那么甲乙两份都需要做修改,怎么解决呢?

老师出一份考卷,打印多分,让学生填答案即可。就是抽出一个公共的父类,将公共的逻辑都都上升到父类当中,而不是让每个子类去重复

提炼后的代码

提炼后的代码的UML图
10.考题抄错会做也白搭-模板方法模式 (大话设计模式Kotlin版)_第3张图片

提取的公共试卷类

/**
 * @create on 2020/4/24 14:43
 * @description 提取公共的模板类
 * @author mrdonkey
 */
abstract class TestPaper {
    /**
     * 试题1
     */
    fun testQuestion1() {
        println("杨过得到,后来给了郭靖,练成倚天剑、屠龙刀的玄铁可能是[] a.球磨铸铁 b.马口铁 c.高速合金钢 d.碳素纤维")
        println("答案:${answer1()}")
    }

    /**
     * 试题2
     */
    fun testQuestion2() {
        println("杨过、程英、陆无双铲除了情花,造成[] a.使这种植物不再害人 b.使一种珍稀物种灭绝 c.破坏了那个生物圈的生态平衡 d.造成该地区沙漠化")
        println("答案:${answer2()}")
    }

    /**
     * 试题1
     */
    fun testQuestion3() {
        println("蓝凤凰致使华山师徒、桃谷六仙呕吐不止,如果你是大夫,会给他们开什么药?[] a.阿司匹林 b.牛黄解毒片 c.氟哌酸 d.让他们喝大量的生牛奶 e.以上全不对")
        println("答案:${answer3()}")
    }

    protected abstract fun answer1(): String

    protected abstract fun answer2(): String

    protected abstract fun answer3(): String

}

学生甲的试卷只需填答案即可(具体的题目都复印出来了(在父类中写好了))

 * @create on 2020/4/24 14:48
 * @description 甲抄的试卷
 * @author mrdonkey
 */
class TestPaperA : TestPaper() {
    override fun answer1(): String {
        return "b"
    }

    override fun answer2(): String {
        return "c"
    }

    override fun answer3(): String {
        return "a"
    }
}

学生乙的考卷

/**
 * @create on 2020/4/24 14:48
 * @description 乙抄的试卷
 * @author mrdonkey
 */
class TestPaperB : TestPaper() {
    override fun answer1(): String {
        return "c"
    }

    override fun answer2(): String {
        return "a"
    }

    override fun answer3(): String {
        return "a"
    }
}

客户端测试

/**
 * @create on 2020/4/24 14:54
 * @description 客户端
 * @author mrdonkey
 */
class Client {
    companion object {
        @JvmStatic
        fun main(vararg args: String) {
            println("甲抄的试卷")
            val paperA: TestPaper = TestPaperA()
            paperA.testQuestion1()
            paperA.testQuestion2()
            paperA.testQuestion3()
            println("乙抄的试卷")
            val paperB: TestPaper = TestPaperB()
            paperB.testQuestion1()
            paperB.testQuestion2()
            paperB.testQuestion3()
        }
    }
}

测试结果同上。

模板方法

当我们需要完成在某一细节层次的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑用模板方法模式来处理。(如上的试卷题目相同,但是学生的答案可能不同)
模板方法模式定义一个操作中的算法骨架,而将一系列步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

模板方法的UML图
10.考题抄错会做也白搭-模板方法模式 (大话设计模式Kotlin版)_第4张图片
具体代码:
Abstract抽象类

/**
 * @create on 2020/4/24 14:59
 * @description 抽象模板类
 * @author mrdonkey
 */
abstract class AbstractClass {

    /**
     * 模板方法,给出了逻辑的骨架而逻辑的组成是一些相应的抽象类
     * 他们都推迟到子类实现
     */
    fun templateMethod() {

    }
    //原始操作1
    abstract fun primitiveOperation1()
    //原始操作2
    abstract fun primitiveOperation2()
}

具体子类A

/**
 * @create on 2020/4/24 15:04
 * @description 具体的子类实现特定的逻辑操作
 * @author mrdonkey
 */
class ConcreteClassA : AbstractClass() {
    override fun primitiveOperation1() {
        println("具体类A 方法1的实现")
    }

    override fun primitiveOperation2() {
        println("具体类A 方法2的实现")
    }
}

具体子类B

/**
 * @create on 2020/4/24 15:04
 * @description 具体的子类实现特定的逻辑操作
 * @author mrdonkey
 */
class ConcreteClassB : AbstractClass() {
    override fun primitiveOperation1() {
        println("具体类B 方法1的实现")
    }

    override fun primitiveOperation2() {
        println("具体类B 方法2的实现")
    }
}

客户端测试

/**
 * @create on 2020/4/24 15:09
 * @description 客户端
 * @author mrdonkey
 */
class Client {
    companion object {
        @JvmStatic
        fun main(vararg args: String) {
            val concreteA: AbstractClass = ConcreteClassA()
            concreteA.primitiveOperation1()
            concreteA.primitiveOperation2()
            val concreteB: AbstractClass = ConcreteClassB()
            concreteB.primitiveOperation1()
            concreteB.primitiveOperation2()
        }
    }
}

测试结果:

具体类A 方法1的实现
具体类A 方法2的实现
具体类B 方法1的实现
具体类B 方法2的实现

模板方法的特点

  • 通过把不变的行为搬移到超类,去除子类中的重复代码来体现它的优势。
  • 提供了一个很好的代码复用平台。
  • 当不变和可变的行为在方法的子类实现中混合在一起,不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就能帮助子类摆脱不变行为的纠缠。

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