10.考題抄錯會做也白搭-模板方法模式 (大話設計模式Kotlin版)

面試題

老師佈置了一次金庸小說的考卷試題,要求甲乙學生抄題並給出對應答案,請用編程語言實現它。

重複=易錯+難改

  • 下面實現的UML圖
    在這裏插入圖片描述

這不簡單嘛,分別創建兩個類:甲抄的試卷、乙抄的試卷

  • 甲抄的試卷
/**
 * @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圖
    提煉後的UML圖

  • 提取的公共試卷類

/**
 * @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圖
    模板方法模式的UML圖
    具體代碼:
  • 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的實現

模板方法的特點

  • 通過把不變的行爲搬移到超類,去除子類中的重複代碼來體現它的優勢。
  • 提供了一個很好的代碼複用平臺。
  • 當不變和可變的行爲在方法的子類實現中混合在一起,不變的行爲就會在子類中重複出現。我們通過模板方法模式把這些行爲搬移到單一的地方,這樣就能幫助子類擺脫不變行爲的糾纏。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章