設計模式-行爲模式之Template Method

更多請移步:我的博客

意圖

Template Method 時行爲模式的一種,讓你定義一個算法的骨架,允許子類重新定義在不改變結構的情況下重新定義算法的某些步驟。

問題

假設你正在寫一個挖掘辦公文檔數據的應用程序。用戶想要輸入各種格式的文件(PDF,DOC,CSV),程序輸出給他們有用的格式化數據。

第一個版本你僅支持DOC文件。下一個版本支持CSV格式文件。一個月後,你又增加了從PDF中分析數據的功能。

這時候,你注意到這三種轉換算法有很多相似之處。他們除了處理不同的文件格式外,對數據的萃取和分析都是一致的。這點對減少重複代碼和算法獨立很有幫助。

客戶端代碼使用這些算法還有另一個問題。選擇合適的行爲的許多條件都需要依賴選擇的算法。如果這三個轉換類都遵循一個通用接口或者一個基類,這些條件就可以用多態消滅掉。

解決

Template Method模式建議把一個算法分成幾個步驟,把步驟封裝到方法中並在一個“template”方法中挨個調用。

子類可以複寫特定的步驟,但是無法修改模板方法,保持算法的結構不變。

在我們的應用中,我們爲所有的轉換算法創建一個通用的基類。模版方法的關鍵步驟看起來像這樣子:

method templateMethod() is
    openDocument();
    extractRawData();
    parseRawData();
    analyzeData();
    closeDocument();

首先,你要把模版方法聲明爲abstract的,讓所有的步驟都需要子類實現。然後,在子類的所有方法都實現後,再把子類共有的部分放到基類中,並使這些步驟(方法)成爲可選的複寫方法。

在我們的例子中,打開和關閉文檔的操作對三種文件方式都不同,所以這點不需要默認實現。但是其他的步驟,比如,轉換和分析,可以放到基類中,使得子類間共享這些代碼。

還有另外一種步驟,叫做鉤子。鉤子時可選的步驟,他默認是空的。因此,即使一個模板沒有腹瀉夠愛方法,它依然可以工作。通常,鉤子被用來再算法開始之前或者結束之後給子類提供機會來做一些事情。

真實世界的類比

大衆住房建設

建築商使用模板方法來進行大規模住房建設。有一個標準的建築模板來描述施工步驟:打地基,築樁,壘牆,走水電等。

但是,儘管標準化,建築商可以稍微改變每一步,使一個房子有點不同(即添加更多的窗口,用不同的顏色塗牆…)

結構

structure

  1. Abstract class聲明瞭算法的每個步驟,以及用來統籌調用每個步驟的模版方法。這些步驟可以被聲明爲abstract或者有默認的實現。

  2. Concrete class實現模版方法中定義的每個抽象步驟,但是它也可以複寫抽象類中的默認實現。具體的實現類不能自己複寫模板方法。

適用性

  • 當子類能夠擴展基本算法而不能修改他的結構時。

    模版方法模式把算法分割成幾個定義在基類中的個性化的步驟,可以被子類輕鬆擴展,並保持算法的結構。

  • 當你需要幾個類來做相似的事情但是又有一些不同時時。當你修改其中一個類時,你必須同時修改其他的類。

    模版方法模式把相似的算法步驟抽取到基類中。代碼的不同部分保留在各個子類中。

如何實現

  1. 分析算法,看他們是否可以被分解成幾個步驟。這些步驟中那些事所有子類公用的,哪些事子類唯一的。

  2. 創建抽象基類,並且聲明模版方法。列出這些算法的結構,把他們聲明爲abstract。考慮哪些方法應當被聲明爲final來阻止子類的重寫。

  3. 如果所有的步驟都是抽象的那就好了。然而,一些步驟適合有默認實現。子類必須實現哪些抽象的方法。

  4. 考慮在合適的步驟之間增加鉤子,以及在模板方法的開始和結束之間添加鉤子。

  5. 算法的每個變種都從抽象類繼承。變種必須實現所有必要的步驟,但是可以選擇性重寫可選步驟。

優點

  • 幫助消除重複代碼。

缺點

  • 你將受限於現有算法的架構。

  • 如果通過子類複寫默認的步驟實現就違背了里氏替換原則。

  • 模板方法往往難以保持更多的步驟。

和其他模式的關係

  • Factory Method是專業化的Template Method。另一方面,Factory Method通常作爲大型Template Method的一個步驟。

  • Template Method採用繼承方式來擴展不同的類取修改算法。Strategy採用修改委託對象行爲的方式來改變對象的行爲。Template Method工作在類級別。Strategy允許你更改單個對象的行爲。

小結

Java核心庫中有些例子:

  • java.io.InputStream,java.io.OutputStream,java.io.Reader和java.io.Writer中所有非抽象的方法。

  • java.util.AbstractList,java.util.AbstractSet和java.util.AbstractMap中所有非抽象的方法。

  • javax.servlet.http.HttpServlet中所有的doXXX()方法都默認響應一個HTTP 405 “Method Not Allowed”錯誤。你可以自由複寫他們。

參考

翻譯整理自:https://refactoring.guru/design-patterns/template-method

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章