31模板方法(Template Method)
無處不在的Template Method?
??? 如果你只想掌握一種設計模式,那么它就是Template Method!
動機(Motivate):
??? 變化 -----是軟件設計的永恒主題,如何管理變化帶來的復雜性?設計模式的藝術性和復雜度就在于如何
分析,并發現系統中的變化和穩定點,并使用特定的設計方法來應對這種變化。
意圖(Intent):
????定義一個操作中的算法的骨架,而將一些步驟延遲到子類中。Template Method使得子類可以不改變一個算法的結構即可重定義該算法的某些特定步驟。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? -------《設計模式》GOF
結構圖(Struct):
??? ?? ?? ?? ?? ????
適用性:
????
1.一次性實現一個算法的不變的部分,并將可變的行為留給子類來實現。
2.各子類中公共的行為應被提取出來并集中到一個公共父類中以避免代碼重復。這是Opdyke和Johnson所描述過的“重分解以一般化”的一個很好的例子。首先識別現有代碼中的不同之處,并且將不同之處分離為新的操作。最后,用一個調用這些新的操作的模板方法來替換這些不同的代碼。
3.控制子類擴展。模板方法只在特定點調用“Hook”操作,這樣就只允許在這些點進行擴展。
生活中的例子:
??? ?? ?? ?? ???
代碼實現:
??? 假如我們需要簡單的讀取Northwind數據庫中的表的記錄并顯示出來。對于數據庫操作,我們知道不管讀取的是哪張表,它一般都應該經過如下這樣的幾步:
1.連接數據庫(Connect)
2.執行查詢命令(Select)
3.顯示數據(Display)
4.斷開數據庫連接(Disconnect)
這些步驟是固定的,但是對于每一張具體的數據表所執行的查詢卻是不一樣的。顯然這需要一個抽象角色,給出頂級行為的實現。如下圖:???????????????????????????????????????????????????????????????????????????????????????????????????
?????????????????????????
Template Method模式的實現方法是從上到下,我們首先給出頂級框架DataAccessObject的實現邏輯:
?1?public?abstract?class?DataAccessObject
?2?
?3?{
?4?????protected?string?connectionString;
?5?
?6?????protected?DataSet?dataSet;
?7?
?8?????protected?virtual?void?Connect()
?9?
10?????{?
11?????????connectionString?=?
12?
13?????????????"Server=.;User?Id=sa;Password=;Database=Northwind";
14?
15?????}
16?
17????protected??abstract?void?Select();
18?
19?????protected?abstract?void?Display();
20?
21?
22?????protected?virtual?void?Disconnect()
23?
24?????{
25?????????connectionString?=?"";
26?????}
27?
28?????//?The?"Template?Method"?
29?
30?????public?void?Run()
31?
32?????{
33?????????Connect();
34?
35?????????Select();
36?
37?????????Display();
38?
39?????????Disconnect();
40?????}
41?}
顯然在這個頂級的框架DataAccessObject中給出了固定的輪廓,方法Run()便是模版方法,Template Method模式也由此而得名。而對于Select()和Display()這兩個抽象方法則留給具體的子類去實現,如下圖:
??? ?? ?????????????
?1?class?Categories?:?DataAccessObject
?2?
?3?{
?4?????protected?override?void?Select()
?5?????{
?6?????????string?sql?=?"select?CategoryName?from?Categories";
?7?
?8?????????SqlDataAdapter?dataAdapter?=?new?SqlDataAdapter(
?9?
10?????????????sql,?connectionString);
11?
12?????????dataSet?=?new?DataSet();
13?
14?????????dataAdapter.Fill(dataSet,?"Categories");
15?
16?????}
17?
18?????protected?override?void?Display()
19?
20?????{
21?
22?????????Console.WriteLine("Categories?----?");
23?
24?????????DataTable?dataTable?=?dataSet.Tables["Categories"];
25?
26?????????foreach?(DataRow?row?in?dataTable.Rows)
27?
28?????????{
29?
30?????????????Console.WriteLine(row["CategoryName"].ToString());
31?
32?????????}
33?
34?????????Console.WriteLine();
35?
36?????}
37?}
?
?1?class?Products?:?DataAccessObject
?2?
?3?{
?4?????protected?override?void?Select()
?5?
?6?????{
?7?????????string?sql?=?"select?top?10?ProductName?from?Products";
?8?
?9?????????SqlDataAdapter?dataAdapter?=?new?SqlDataAdapter(
10?
11?????????????sql,?connectionString);
12?
13?????????dataSet?=?new?DataSet();
14?
15?????????dataAdapter.Fill(dataSet,?"Products");
16?
17?????}
18?
19?????protected?override?void?Display()
20?
21?????{
22?
23?????????Console.WriteLine("Products?----?");
24?
25?????????DataTable?dataTable?=?dataSet.Tables["Products"];
26?
27?????????foreach?(DataRow?row?in?dataTable.Rows)
28?
29?????????{
30?????????????Console.WriteLine(row["ProductName"].ToString());
31?
32?????????}
33?
34?????????Console.WriteLine();
35?
36?????}
37?
38?}
再來看看客戶端程序的調用,不需要再去調用每一個步驟的方法:
?1?public?class?App
?2?
?3?{
?4?????static?void?Main()
?5?????{
?6?
?7?????????DataAccessObject?dao;
?8?
?9?
10?????????dao?=?new?Categories();
11?
12?????????dao.Run();
13?
14?
15?????????dao?=?new?Products();
16?
17?????????dao.Run();
18?
19?????????//?Wait?for?user?
20?
21?????????Console.Read();
22?
23?????}
24?
25?}
?
在上面的例子中,需要注意的是:
1.對于Connect()和Disconnect()方法實現為了virtual,而Select()和Display()方法則為abstract,這是因為如果這個方法有默認的實現,則實現為virtual,否則為abstract。
2.Run()方法作為一個模版方法,它的一個重要特征是:在基類里定義,而且不能夠被派生類更改。有時候它是私有方法(private method),但實際上它經常被聲明為protected。它通過調用其它的基類方法(覆寫過的)來工作,但它經常是作為初始化過程的一部分被調用的,這樣就沒必要讓客戶端程序員能夠直接調用它了。
3.在一開始我們提到了不管讀的是哪張數據表,它們都有共同的操作步驟,即共同點。因此可以說Template Method模式的一個特征就是剝離共同點。
Template Mehtod實現要點:
1.Template Method模式是一種非常基礎性的設計模式,在面向對象系統中有著大量的應用。它用最簡潔的機制(虛函數的多態性)為很多應用程序框架提供了靈活的擴展點,是代碼復用方面的基本實現結構。
2.除了可以靈活應對子步驟的變化外,“不用調用我,讓我來調用你(Don't call me ,let me call you)”的反向控制結構是Template Method的典型應用。“Don’t call me.Let me call you”是指一個父類調用一個子類的操作,而不是相反。
3.在具體實現方面,被Template Method調用的虛方法可以具有實現,也可以沒有任何實現(抽象方法,純虛方法),但一般推薦將它們設置為protected方法。
總結
以上是生活随笔為你收集整理的31模板方法(Template Method)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华体转债(754679)什么时候申购并公
- 下一篇: 第二十三节: EF性能篇(三)之基于开源