意圖
定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。Template Method使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。
場景
模版方法是非常容易理解的設計模式,一來是因為它沒有過多結構上的交錯,二來是因為這種代碼復用技術對於掌握OO知識的人來說非常容易可以想到,很可能你已經在很多地方運用了模版方法。在運用一些設計模式的時候常常也會一起運用模版方法,甚至有的設計模式本身就帶有模版方法的思想。
今天,我們給出這樣一個實際的例子。做過銀行支付、支付寶支付的人都知道,一個支付的過程是基於兩個接口的。提交接口和網關返回接口,雖然各大網關的支付接口格式不同,比如有的網關對於支付金額的參數是money,有的網關又是amount,但是從支付的提交過程來說,我們一般都會經歷以下步驟:
l 獲取訂單信息,驗證訂單的合法性
l 生成用於提交到各大網關的表單
l 記錄日志
l 把表單提交到相應的網關
對於各個網關,生成的提交表單以及記錄日志的方式是不一樣的,但是整個支付流程以及流程中的獲取訂單信息、提交表單的過程是一樣的。由此引入模版方法模式來復用不變的部分,把可變的部分留給子類去實現。
示例代碼
using System;
using System.Collections.Generic;
using System.Text;
namespace TemplateMethodExample
{
class Program
{
static void Main(string[] args)
{
PayGateway pg = new IPSGateway();
pg.SubmitOrder(new Order());
}
}
class Order
{
}
class SubmitForm
{
}
abstract class PayGateway
{
protected abstract void WriteLog(SubmitForm sf);
protected abstract SubmitForm GenerateOrderForm(Order order);
public void SubmitOrder(Order order)
{
if (order == null )
{
Console.WriteLine("Invalid Order");
return;
}
SubmitForm sf = GenerateOrderForm(order);
if (sf == null )
{
Console.WriteLine("Generate Submit Form Failed");
return;
}
WriteLog(sf);
}
}
class IPSGateway : PayGateway
{
protected override void WriteLog(SubmitForm sf)
{
Console.WriteLine("Log Wrote");
}
protected override SubmitForm GenerateOrderForm(Order order)
{
Console.WriteLine("Submit Form Generated");
return new SubmitForm();
}
}
}
代碼執行結果如下圖:
代碼說明
l PayGateway類型是抽象模版角色。它定義了支付過程不變的部分,並且把變化部分定義為抽象操作,讓子類去實現。其中的SubmitOrder方法是模版方法。
l IPSGateway類型是具體模版角色。它代表了某一種支付網關,並且按照這種支付網關的接口標准來實現生成提交表單和記錄日志的操作。
何時采用
l 如果某些類型的操作擁有共同的實現骨架和不同的實現細節的話,可以考慮使用模版方法來封裝統一的部分。
實現要點
l 復用算法的骨架,將可變的實現細節留給子類實現。
l 留給子類實現的方法需要在父類中定義,可以是抽象方法也可以是帶有默認實現的方法。
注意事項
l 模版方法可以說是最不像設計模式的設計模式,通常很多設計模式會和模版方法一起使用。