天天看點

設計模式【13】-- 模闆模式怎麼弄?

開局還是那種圖,各位客官往下看...

張無忌學太極拳,忘記了所有招式,打倒了"玄冥二老",所謂"心中無招"。設計模式可謂招數,如果先學通了各種模式,又忘掉了所有模式而随心所欲,可謂OO之最高境界。

模闆模式是什麼?

模闆模式,同樣是一種行為型模式,也就是關于對象做什麼或者怎麼做的設計模式。模闆模式的本質需要定義操作中的算法的架構,但是有一些步驟,又不需要具體的實作,而是不同的子類各自實作。子類不能修改流程架構,但是部分的步驟可以做定制化的實作。

主要要解決一個問題:一些通用的方法,但是每一個子類卻都重新寫,備援。

比如說,做菜的步驟一般是:

洗鍋 --> 炒菜 --> 洗碗

,不同的菜,隻是炒菜這一個步驟具體細節是不同的,但是其他步驟确實幾乎一模一樣的,這樣其實整體架構,以及重複的步驟,我們可以抽象到模闆中,而不同的細節方法可以開放給每一種菜(具體實作)去定制。

又比如造房子的時候,很多地方的建造都是一樣的:地基,牆壁,水管等等,但是不同的房子裡面的内部的設計又有所不同。

不使用模闆模式

就挑個簡單的例子“炒菜”,如果不使用模闆模式的話,糖醋鯉魚:

public class SweetAndSourCarp {

    public void cookFood(){
        washPan();
        cook();
        eat();
        washDishes();
        System.out.println("");
    }

    private void washPan(){
        System.out.print("洗鍋 --> ");
    }

    private void cook(){
        System.out.print("煮糖醋鯉魚 --> ");
    }

    private void eat(){
        System.out.print("吃飯 --> ");
    }

    private void washDishes(){
        System.out.print("洗碗 --> ");
    }
}           

複制

再弄一個農家小炒肉,需要寫很多相同的方法:

public class ShreddedPorkWithVegetables {

    public void cookFood(){
        washPan();
        cook();
        eat();
        washDishes();
        System.out.println("");
    }

    private void washPan(){
        System.out.print("洗鍋 --> ");
    }

    private void cook(){
        System.out.print("炒農家小炒肉 --> ");
    }

    private void eat(){
        System.out.print("吃飯 --> ");
    }

    private void washDishes(){
        System.out.print("洗碗 --> ");
    }
}           

複制

測試類如下:

public class Test {
    public static void main(String[] args) {
        SweetAndSourCarp sweetAndSourCarp = new SweetAndSourCarp();
        sweetAndSourCarp.cookFood();

        ShreddedPorkWithVegetables shreddedPorkWithVegetables = new ShreddedPorkWithVegetables();
        shreddedPorkWithVegetables.cookFood();
    }
}           

複制

測試結果:

洗鍋 --> 煮糖醋鯉魚 --> 吃飯 --> 洗碗 --> 
洗鍋 --> 炒農家小炒肉 --> 吃飯 --> 洗碗 -->            

複制

可以看到,整體流程是一樣的,有些步驟一樣,有些步驟不一樣,但是不使用模闆模式,需要每個類都重寫一遍方法,即使是通用方法,整個流程都需要自己寫一遍。

使用模闆模式優化

如果使用模闆模式,那麼我們會抽象出一個抽象類,定義整體的流程,已經固定的步驟,開放需要定制的方法,讓具體的實作類按照自己的需求來定制。

設計模式【13】-- 模闆模式怎麼弄?

定義的抽象類:

public abstract class CookFood {
    public final void cookFood() {
        washPan();
        cook();
        eat();
        washDishes();
        System.out.println("");
    }

    private final void washPan() {
        System.out.print("洗鍋 --> ");
    }

    public abstract void cook();

    private final void eat() {
        System.out.print("吃飯 --> ");
    }

    private final void washDishes() {
        System.out.print("洗碗 --> ");
    }
}           

複制

具體的實作類糖醋鯉魚:

public class SweetAndSourCarp extends CookFood {
    @Override
    public void cook() {
        System.out.print("煮糖醋鯉魚 --> ");
    }
}           

複制

農家小炒肉:

public class ShreddedPorkWithVegetables extends CookFood {
    @Override
    public void cook() {
        System.out.print("炒農家小炒肉 --> ");
    }
}           

複制

測試類與前面的一樣,測試結果也一樣,這裡不再重複。

上面的方法中,其實我們隻開放了

cook()

方法,這就是鈎子方法:

在模闆方法模式的父類中,我們可以定義一個方法,它預設不做任何事,子類可以視情況要不要覆寫它,該方法稱為 ”鈎子方法”

鈎子方法是開放的,可以由子類随意覆寫,但是像上面的其他方法,我們不希望子類重寫或者覆寫它,就可以用

final

關鍵字,防止子類重寫模闆方法。

模闆模式的應用

其實在

JDK

Thread

實作中,就是使用了模闆模式,我們知道建立線程有兩個方式:

  • 建立

    Thread

  • 實作

    runnable

    接口

我們實作的一般是

run()

方法, 但是調用的卻是

start()

方法來啟動線程,這個原因就是

start()

方法裡面幫我們調用了

run()

方法,

run()

方法是開發的方法,我們可以覆寫重寫它。

設計模式【13】-- 模闆模式怎麼弄?

Start0()

是一個

native

方法,是由 c 語言去實作的,在調用的時候,真正調用了我們的

run()

方法,如果需要跟蹤這個方法需要到

HotSpot

底層去。這裡介紹的目的是讓大家了解,它同樣是使用了模闆模式。

private native void start0();           

複制

了解

native

關鍵字可以參考:http://aphysia.cn/archives/native

模闆模式的優缺點

模闆模式的優點:

  • 1、封裝固定的部分,拓展需要定制修改的部分,符合開閉原則。
  • 2、公共的代碼在父類中,容易維護。
  • 3、整個流程由父類把握,調整比較友善。

缺點:

  • 1、子類可能會很多,系統複雜度上升。
  • 2、子類隻有一小部分實作,了解全部方法則需要在父類中閱讀,影響代碼閱讀。

總結:代碼該隐藏的複雜細節隐藏起來,開放定制化部分,優雅!

設計模式系列:

  • 設計模式【1】-- 單例模式到底幾種寫法?
  • 設計模式【1.1】-- 你想如何破壞單例模式?
  • 設計模式【1.2】-- 枚舉式單例有那麼好用麼?
  • 設計模式【1.3】-- 為什麼餓漢式單例是線程安全的?
  • 設計模式【2】-- 簡單工廠模式了解一下?
  • 設計模式【2.1】-- 簡單工廠模式怎麼演變成工廠方法模式?
  • 設計模式【2.2】-- 工廠模式怎麼演變成抽象工廠模式?
  • 設計模式【3.1】-- 淺談代理模式之靜态、動态、cglib代理
  • 設計模式【3.2】-- JDK動态代理源碼分析有多香?
  • 設計模式【3.3】-- CGLIB動态代理源碼解讀
  • 設計模式【4】-- 建造者模式詳解
  • 設計模式【5】-- 原型模式
  • 設計模式【6.1】-- 初探擴充卡模式
  • 設計模式【6.2】-- 再聊聊擴充卡模式
  • 設計模式【7】-- 探索一下橋接模式
  • 設計模式【8】-- 手工耿教我寫裝飾器模式
  • 設計模式【9】-- 外觀模式?沒那麼高大上
  • 設計模式【10】-- 順便看看享元模式
  • 設計模式【11】-- 搞定組合模式
  • 設計模式【12】-- 搞定最近大火的政策模式