.Net行為型設(shè)計(jì)模式之模板方法模式(Template?Method)
一、動(dòng)機(jī)(Motivate)
“模板方法”,就是有一個(gè)方法包含了一個(gè)模板,這個(gè)模板是一個(gè)算法。在我們的現(xiàn)實(shí)生活中有很多例子可以拿來(lái)說(shuō)明這個(gè)模式,就拿吃餃子這個(gè)事情來(lái)說(shuō),要想吃到餃子必須經(jīng)過(guò)三步,第一步是“和面”,第二步是“包餡”,第三步是“煮餃子”,這三步就是一個(gè)算法,我們要想吃到不同的面和餡的餃子,對(duì)這三步中的任意一步就行操作就可以,也可以完全定義這三步
在軟件構(gòu)建過(guò)程中,對(duì)于某一項(xiàng)任務(wù),它常常有穩(wěn)定的整體操作結(jié)構(gòu),但各個(gè)子步驟卻有很多改變的需求,或者由于固有的原因(比如框架與應(yīng)用之間的關(guān)系)而無(wú)法和任務(wù)的整體結(jié)構(gòu)同時(shí)實(shí)現(xiàn)。如何在確定穩(wěn)定操作結(jié)構(gòu)的前提下,來(lái)靈活應(yīng)對(duì)各個(gè)子步驟的變化或者晚期實(shí)現(xiàn)需求?
二、意圖(Intent)
定義一個(gè)操作中的算法的骨架,而將一些步驟延遲到子類(lèi)中。Template Method使得子類(lèi)可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟。 ——《設(shè)計(jì)模式》GoF
三、結(jié)構(gòu)圖
四、模式的組成
模板方法模式參與者:
(1)、抽象類(lèi)角色(AbstractClass):定義一個(gè)模板方法(TemplateMethod),在該方法中包含著一個(gè)算法的骨架,具體的算法步驟是PrimitiveOperation1方法和PrimitiveOperation2方法,該抽象類(lèi)的子類(lèi)將重定義PrimitiveOperation1和PrimitiveOperation2操作。
(2)、具體類(lèi)角色(ConcreteClass):實(shí)現(xiàn)PrimitiveOperation1方法和PrimitiveOperation2方法以完成算法中與特定子類(lèi)(Client)相關(guān)的內(nèi)容。
在模板方法模式中,AbstractClass中的TemplateMethod提供了一個(gè)標(biāo)準(zhǔn)模板,該模板包含PrimitiveOperation1和PrimitiveOperation2兩個(gè)方法,這兩個(gè)方法的內(nèi)容Client可以根據(jù)自己的需要重寫(xiě)。
五、模板方法模式的具體實(shí)現(xiàn)
下面以生活中吃餃子為例來(lái)實(shí)現(xiàn)模板方法模式。在現(xiàn)實(shí)生活中,做餃子的步驟都大致相同,如果我們針對(duì)每種餃子的做法都定義一個(gè)類(lèi),這樣在每個(gè)類(lèi)中都有很多相同的代碼,為了解決這個(gè)問(wèn)題,我們一般的思路肯定是把相同的部分抽象出來(lái)到抽象類(lèi)中去定義,具體子類(lèi)來(lái)實(shí)現(xiàn)具體的不同部分,這個(gè)思路也正式模板方法的實(shí)現(xiàn)精髓所在,具體實(shí)現(xiàn)代碼如下:
static void Main(string[] args) { //現(xiàn)在想吃綠色面的,豬肉大蔥餡的餃子 AbstractClass fan = new ConcreteClass1(); fan.EatDumplings(); Console.WriteLine(); //過(guò)了段時(shí)間,我開(kāi)始想吃橙色面的,韭菜雞蛋餡的餃子 fan = new ConcreteClass2(); fan.EatDumplings(); } //該類(lèi)型就是抽象類(lèi)角色--AbstractClass,定義做餃子的算法骨架,這里有三步驟,當(dāng)然也可以有多個(gè)步驟,根據(jù)實(shí)際需要而定 public abstract class AbstractClass { //該方法就是模板方法,方法里面包含了做餃子的算法步驟,模板方法可以返回結(jié)果,也可以是void類(lèi)型,視具體情況而定 public void EatDumplings() { MakingDough();//和面 MakeDumplings();//包餡 BoiledDumplings(); //煮餃子 Console.WriteLine("餃子真好吃!"); } public abstract void MakingDough();//要想吃餃子第一步肯定是“和面”---該方法相當(dāng)于算法中的某一步 public abstract void MakeDumplings();//要想吃餃子第二部是“包餃子”---該方法相當(dāng)于算法中的某一步 public abstract void BoiledDumplings(); //要想吃餃子第三部是“煮餃子”---該方法相當(dāng)于算法中的某一步 } //該類(lèi)型是具體類(lèi)角色--ConcreteClass,我想吃綠色面皮,豬肉大蔥餡的餃子 public sealed class ConcreteClass1 : AbstractClass { public override void MakingDough() //要想吃餃子第一步肯定是“和面”---該方法相當(dāng)于算法中的某一步 { Console.WriteLine("在和面的時(shí)候加入芹菜汁,和好的面就是綠色的");//我想要面是綠色的,綠色健康嘛,就可以在此步定制了 } public override void MakeDumplings()//要想吃餃子第二部是“包餃子”---該方法相當(dāng)于算法中的某一步 { Console.WriteLine("農(nóng)家豬肉和農(nóng)家大蔥,制作成餡");//我想吃豬肉大蔥餡的,在此步就可以定制了 } public override void BoiledDumplings()//要想吃餃子第三部是“煮餃子”---該方法相當(dāng)于算法中的某一步 { Console.WriteLine("用我家的大鐵鍋和大木材煮餃子");//我想吃大鐵鍋煮的餃子,有家的味道,在此步就可以定制了 } } //該類(lèi)型是具體類(lèi)角色--ConcreteClass2,我想吃橙色面皮,韭菜雞蛋餡的餃子 public sealed class ConcreteClass2 : AbstractClass { public override void MakingDough()//要想吃餃子第一步肯定是“和面”---該方法相當(dāng)于算法中的某一步 { Console.WriteLine("在和面的時(shí)候加入胡蘿卜汁,和好的面就是橙色的");//我想要面是橙色的,加入胡蘿卜汁就可以。在此步定制就可以了。 } public override void MakeDumplings() //要想吃餃子第二部是“包餃子”---該方法相當(dāng)于算法中的某一步 { Console.WriteLine("農(nóng)家雞蛋和農(nóng)家韭菜,制作成餡"); //我想吃韭菜雞蛋餡的,在此步就可以定制了 } public override void BoiledDumplings()//要想吃餃子第三部是“煮餃子”---該方法相當(dāng)于算法中的某一步 { Console.WriteLine("可以用一般煤氣和不粘鍋煮就可以"); //此處沒(méi)要求 } }
六、模板方法模式的實(shí)現(xiàn)要點(diǎn):
Template Method模式是一種非常基礎(chǔ)性的設(shè)計(jì)模式,在面向?qū)ο笙到y(tǒng)中有著大量的應(yīng)用。它用最簡(jiǎn)潔的機(jī)制(虛函數(shù)的多態(tài)性)為很多應(yīng)用程序框架提供了靈活的擴(kuò)展,是代碼復(fù)用方面的基本實(shí)現(xiàn)結(jié)構(gòu)。除了可以靈活應(yīng)對(duì)子步驟的變化外,“Don't call me, let me call you(不要調(diào)用我,讓我來(lái)調(diào)用你)”的反向控制結(jié)構(gòu)是Template Method的典型應(yīng)用。
1、模板方法模式適用情形:
(1)、一次性實(shí)現(xiàn)一個(gè)算法的不變部分,并將可變的行為留給子類(lèi)來(lái)實(shí)現(xiàn)。
(2)、各子類(lèi)中公共的行為應(yīng)被提取出來(lái)并集中到一個(gè)公共父類(lèi)中以避免代碼重復(fù)。
(3)、控制子類(lèi)擴(kuò)展。模板方法只允許在特定點(diǎn)進(jìn)行擴(kuò)展,而模板部分則是穩(wěn)定的。
2、模板方法模式特點(diǎn):
(1)、TemplateMethod模式是一種非?;A(chǔ)性的設(shè)計(jì)模式,在面向?qū)ο笙到y(tǒng)中大量應(yīng)用。它用最簡(jiǎn)潔的機(jī)制(基礎(chǔ)、多態(tài))為很多應(yīng)用程序框架提供了靈活的擴(kuò)展點(diǎn),是代碼復(fù)用方面的基本實(shí)現(xiàn)結(jié)構(gòu)。
(2)、在具體實(shí)現(xiàn)方面,被TemplateMethod調(diào)用的虛方法可以具有實(shí)現(xiàn),也可以沒(méi)有任何實(shí)現(xiàn)(抽象方法或虛方法)。但一般推薦將它們?cè)O(shè)置為protected方法使得只有子類(lèi)可以訪問(wèn)它們。
(3)、模板方法模式通過(guò)對(duì)子類(lèi)的擴(kuò)展增加新的行為,符合“開(kāi)閉原則”。
七、.NET 中模板模式的實(shí)現(xiàn)
這種模式在控件設(shè)計(jì)中大量的用到,比如:控件有自己的生命周期,Page對(duì)象也有自己的生命周期,Application應(yīng)用對(duì)象也有自己的生命周期,這個(gè)生命周期里面的每個(gè)階段其實(shí)就是模板方法里面包含的每個(gè)步驟,這些階段步驟會(huì)被一個(gè)方法包含著,這個(gè)方法就是“模板方法”。讓我們?cè)僬f(shuō)說(shuō)控件吧,因?yàn)閷?xiě)好的控件,可能需要被開(kāi)發(fā)人員自定義,那么在控件里我們已經(jīng)定義好了控件呈現(xiàn)、動(dòng)作的骨架,但是有些自定義的需求,需要延遲到擴(kuò)展控件的開(kāi)發(fā)人員來(lái)決定。
當(dāng)我們?cè)谧鯳indows應(yīng)用程序的時(shí)候,就會(huì)使用Windows控件,那Windows控件是如何顯示在Windows Form 上的呢,就需要一個(gè)OnPaint方法把控件畫(huà)出來(lái),這里OnPaint是一個(gè)虛方法的子步驟,這就是一個(gè)Template Method設(shè)計(jì)模式。如果我們不去重寫(xiě)這個(gè)OnPaint方法,它就有一個(gè)基本的默認(rèn)實(shí)現(xiàn),畫(huà)一個(gè)空窗體。這里我們并沒(méi)有調(diào)用OnPaint方法,而是Application的Run會(huì)進(jìn)入Windows的消息循環(huán)結(jié)構(gòu),Paint就是一個(gè)消息。當(dāng)我們移動(dòng)一下窗口都會(huì)導(dǎo)致Paint事件的發(fā)生,并導(dǎo)致OnPaint函數(shù)的調(diào)用,這就是一種反向調(diào)用。當(dāng)然,還有很多其他的子步驟可以提供擴(kuò)展點(diǎn),例如OnClose等,很多以O(shè)n開(kāi)頭的全部都是Template Method模式的虛方法。 這個(gè)里面內(nèi)容很復(fù)雜,它并不是用一個(gè)Template Method在里面調(diào)用所有的子步驟方法,它實(shí)際上是把整體的Template Method方法置于了一個(gè)消息循環(huán)的結(jié)構(gòu)里面,我們可以把消息循環(huán)的結(jié)構(gòu)看做模板方法里面的TemplateMethod公有非虛方法。
到此這篇關(guān)于.Net行為型設(shè)計(jì)模式之模板方法模式(Template Method)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
ASP.NET Core應(yīng)用啟動(dòng)Startup類(lèi)簡(jiǎn)介
這篇文章介紹了ASP.NET Core中的應(yīng)用啟動(dòng)Startup類(lèi),文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04Linux?CentOS下docker部署Asp.Net?Core(.Net6)
這篇文章介紹了Linux?CentOS下docker部署Asp.Net?Core(.Net6)的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12ASP.NET中Literal與Label控件的區(qū)別
相信學(xué)ASP.NET的朋友們都會(huì)遇到Literal和Label到底該用那個(gè)的問(wèn)題,Literal和Label到底有什么不同,其實(shí)簡(jiǎn)單的講就是Literal不會(huì)產(chǎn)生HTML代碼,而Label會(huì)產(chǎn)生一個(gè)span標(biāo)記,下面為大家詳細(xì)講解一下。2016-04-04那些年,我還在學(xué)asp.net(一) 學(xué)習(xí)筆記
那些年到此,基本學(xué)習(xí)了前端的基本知識(shí),那些年的第四課就是asp.net,當(dāng)然那時(shí)看了很多教程,比如說(shuō):天轟穿,當(dāng)然天轟穿說(shuō)得比較多,如面向?qū)ο?,C#知識(shí),由于當(dāng)時(shí)上過(guò)C++,所以就沒(méi)有看這些,直接從asp.net開(kāi)始,主要是學(xué)習(xí)一下asp.net用到的一些基本控件2012-03-03C#簡(jiǎn)單獲取屏幕鼠標(biāo)坐標(biāo)點(diǎn)顏色方法介紹
C#簡(jiǎn)單獲取屏幕鼠標(biāo)坐標(biāo)點(diǎn)顏色方法介紹;有需求的朋友可以參考下2012-11-11ASP.NET中 RadioButtonList 單選按鈕組控件的使用方法
本文主要簡(jiǎn)單介紹RadioButtonList控件的常見(jiàn)屬性和使用方法,希望對(duì)大家有所幫助。2016-04-04連接ACCESS數(shù)據(jù)庫(kù)時(shí)發(fā)生錯(cuò)誤提示:找不到可安裝的 ISAM
連接ACCESS數(shù)據(jù)庫(kù)時(shí)發(fā)生錯(cuò)誤提示:找不到可安裝的 ISAM 檢查后發(fā)現(xiàn)原來(lái)是把Data Source寫(xiě)成 DataSource了2011-04-04