在面向對象設計中,無非就是封裝,繼承之類的運用,而設計出可擴展性好,代碼冗餘少,可重用性高的程序也是衡量面向對象開發的一個標準。面向對象使用至關靈活,雖然核心特徵只有封裝,繼承,多態,可是經過對它們的靈活配合和使用變化出了各類設計模式。能夠說,設計模式是對面向對象的一些經驗總結,今天將看到其中的一個,Template Method(模板方法模式)。算法
定義:定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。Te m p l a t e M e t h o d使得子類設計模式
能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。 (摘自:《設計模式:可複用面向對象軟件的基礎》)ide
那麼如何來理解上述的定義呢?能夠這樣想,模板方法,說到底也是一個方法,而它的做用正如其名--模板。在這個模板裏面,定義了一系列連續的動做。這樣,當調用這個模板方法後,其內部的方法將被按照順序調用。可是,僅此而已的話,只是對一系列方法進行了封裝,根本沒有體現面向對象,其關鍵在定義的後半句,將一些步驟延遲到子類中。模板方法爲了減小代碼冗餘,將子類中相同的操做提取出來,放到抽象父類中,這些方法咱們叫作具體方法(Concrete Method),而若是是不一樣的操做的話,就在抽象類中定義它們的抽象方法(Abstract Method),把具體的實現延遲到子類中去。除此以外,在抽象父類中還有一類方法,這類方法在須要的時候,能夠在子類中實現它們,與抽象方法不一樣的是在子類中比較自由,沒有強制性要求實現,此類方法叫作鉤子方法(HookMethod),爲了區別抽象方法,在定義時通常以Do開頭,例如DoCloseDocument();上述三類方法都將被放到模板方法中(Template Method)。這樣,在客戶端中,咱們調用模板方法,模板方法調用將是不變的,由於在模板方法中的算法結構是固定不變的,可是能夠經過實現不一樣的子類方法,重定義算法的某些特定步驟。spa
上述描述的有點多,看着暈了,仍是以一個簡單的例子再來理解下:設計
記得這是趙本山好久之前的一個小品《鐘點工》裏的一個例子,雖然當年這個節目的時候,我年紀還很小,可是居然還印象深入。這裏,就用這個例子來演示模板方法模式。對象
1、分析此過程繼承
經過此例子,咱們知道,把大象放進冰箱,須要三步(不考慮冰箱中是否有長頸鹿或者冰箱已經滿了,咱們忽略這些):ci
1.打開冰箱開發
2.把大象放進去string
3.關上冰箱
2、抽象出更抽象的過程
能夠放大象,固然能夠放其它東西,好比放小雞,放長頸鹿等等,全部的這些東西咱們就叫東西,這話聽怎麼這麼奇怪,那就書面化點,全部的這些東西咱們叫作對象。
這樣,抽象出的一個基類能夠叫作:Abstract class PutSomethingIntoFridge{}
而後,在基類中咱們把東西放進冰箱的一系列算法步驟寫入Put()的模板方法中。
而後....此處省略100個字,具體看下文代碼
3、繼承父類,實現各類東西放進冰箱
設計完成後,整個結構以下圖所示:
示例代碼:
PutSomethingIntoFridge.cs
using System;
namespace PatternDemo
{
abstract class PutSomethingIntoFridge
{
// 模板方法
public void Put()
{
// 具體方法
OpenFridge();
// Hook方法,擴大冰箱容量
DoEnlargeCapacity();
// 抽象方法
PutInto();
// 具體方法
CloseFridge();
}
// 具體方法,無需在子類中從新實現
protected void OpenFridge()
{
Console.WriteLine( " 打開冰箱 " );
}
// 具體方法,無需在子類中從新實現
protected void CloseFridge()
{
Console.WriteLine( " 關上冰箱 " );
}
// 抽象方法,在子類中強制實現
abstract protected void PutInto();
// Hook方法,擴大冰箱容量,此方法在子類中可選擇實現。換句話說,只有在必要的時候才重寫實現它
virtual protected void DoEnlargeCapacity(){}
}
}
PutChickenIntoFridge.cs
using System;
namespace PatternDemo
{
class PutChickenIntoFridge : PutSomethingIntoFridge
{
// 普通冰箱能放下一隻雞,固然是在冰箱沒有滿的狀況下,因此只要實現具體放的東西方法就行
protected override void PutInto()
{
Console.WriteLine( " 把雞放進冰箱 " );
}
}
}
PutElephantIntoFridge
using System;
namespace PatternDemo
{
class PutElephantIntoFridge : PutSomethingIntoFridge
{
// 因爲大象太大,普通冰箱沒法放進去,須要擴大冰箱容量,因此實現DoEnlargeCapacity方法
protected override void DoEnlargeCapacity()
{
Console.WriteLine( " 擴大冰箱容量10000倍 " );
}
protected override void PutInto()
{
Console.WriteLine( " 把大象放進冰箱 " );
}
}
Client
namespace PatternDemo
{
class Client
{
static void Main( string [] args)
{
PutSomethingIntoFridge putchicken = new PutChickenIntoFridge();
putchicken.Put();
PutSomethingIntoFridge putelephant = new PutElephantIntoFridge();
putelephant.Put();
}
}
模板方法是一種代碼複用的基本技術,它們在類庫中尤其重要,它們提取了類庫中的公共行爲。
模板方法的適用狀況:
• 一次性實現一個算法的不變的部分,並將可變的行爲留給子類來實現。
• 各子類中公共的行爲應被提取出來並集中到一個公共父類中以免代碼重複。首先識別
現有代碼中的不一樣之處,而且將不一樣之處分離爲新的操做。最後,用一個調用這些新的
操做的模板方法來替換這些不一樣的代碼。
• 控制子類擴展。模板方法只在特定點調用「 h o o k」操做(例如這裏舉例的當冰箱裝不下大象這樣的特殊狀況時),這樣就只允
許在這些點進行擴展。
須要注意的地方:
1.爲了保證模板方法中調用的各類原語操做(抽象方法,具體方法,Hook方法)只能被模板方法調用,訪問權限應爲protected。
2.儘可能減小原語操做。以防客戶端代碼冗長。
3.避免亂用模板方法,致使子類氾濫,應根據具體狀況適時的使用。
參考資料
《設計模式:可複用面向對象軟件的基礎》