[Head First设计模式]饺子馆(冬至)中的设计模式——工厂模式
?系列文章
[Head First設(shè)計(jì)模式]山西面館中的設(shè)計(jì)模式——裝飾者模式
[Head First設(shè)計(jì)模式]山西面館中的設(shè)計(jì)模式——觀察者模式
[Head First設(shè)計(jì)模式]山西面館中的設(shè)計(jì)模式——建造者模式
引言
今天是冬至,去餃子館吃餃子,看他們店里面的水餃種類挺多,在等待中,在想是不是可以用設(shè)計(jì)模式模擬一下,生產(chǎn)餃子的過(guò)程,正好最近也在看工廠模式,也就現(xiàn)學(xué)現(xiàn)賣了。當(dāng)然,實(shí)現(xiàn)的方式很多,只是一個(gè)例子而已。祝大家冬至,多多吃水餃.....
對(duì)象創(chuàng)建的問(wèn)題?
我們應(yīng)該面向接口編程而不是面向?qū)崿F(xiàn)編程,因?yàn)槊嫦驅(qū)崿F(xiàn)編程會(huì)使得我們的設(shè)計(jì)更脆弱,缺乏靈活性。但是我們每次使用new時(shí),是不是正在違背這一設(shè)計(jì)原則呢?
當(dāng)我們擁有一組相關(guān)的具體類時(shí),是不是常常被迫寫(xiě)出類似下面的代碼?
1 Duck duck; 2 3 if(picnic){ 4 5 duck=new MallardDuck(); 6 7 }else if(hunting){ 8 9 duck=new DecogDuck(); 10 11 }else if(inBathTub){ 12 13 duck=new RubberDuck(); 14 15 }(以上為偽代碼,只為說(shuō)明問(wèn)題)
向上面的實(shí)例化過(guò)程,知道運(yùn)行時(shí)我們才知道需要實(shí)例化哪個(gè)類。
這樣做的后果是如果應(yīng)用要做變化或擴(kuò)展,往往要修改這段代碼。這使得維護(hù)困難,并容易引入錯(cuò)誤。
問(wèn)題在哪兒?
出現(xiàn)上面那種問(wèn)題,是不是new的問(wèn)題呢?
從技術(shù)上來(lái)說(shuō),new并沒(méi)有任何問(wèn)題。new只是面向?qū)ο笳Z(yǔ)言的最基本部分,真正的問(wèn)題在于“變化”。
如果對(duì)接口編程,我們可以實(shí)現(xiàn)與許多“變化”的隔離,因?yàn)橥ㄟ^(guò)多態(tài)機(jī)制,我們的代碼對(duì)于實(shí)現(xiàn)接口的新類依然適用。但是使用具體類麻煩就來(lái)了,因?yàn)樵黾有碌木唧w類時(shí)相應(yīng)的代碼可能就必須修改?
怎么辦?
面向?qū)ο蟮脑O(shè)計(jì)原則:識(shí)別變化的部分,并將與不變化的部分相分離。
書(shū)中Pizza店案例分析
PizzaStore類中的一段代碼-訂做pizza
?
修改后的代碼
由于市場(chǎng)競(jìng)爭(zhēng),其他pizza店推出了新產(chǎn)品,我們也得增加!例如VeggiePizza。 GreekPizza最近不受歡迎,把它從菜單中取消。
于是。。。
?
分析:變與不變的部分
?
分離
我們將專管制作pizza的對(duì)象叫做Pizza工廠
Pizza工廠---SimplePizzaFactory
思考一下?
????? 這看來(lái)好像我們只是把問(wèn)題從一個(gè)對(duì)象推給了另一個(gè)對(duì)象!這樣做有什么好處?
????? SimplePizzaFactory可以有許多個(gè)客戶,這樣,當(dāng)實(shí)現(xiàn)改變時(shí)我們只需要修改SimplePizzaFactory,而不需修改眾多的客戶。 提高了聚合度,PizzaStore的職責(zé)是使用pizza對(duì)象, SimplePizzaFactory的職責(zé)是決定創(chuàng)建什么樣的pizza對(duì)象。
用工廠重寫(xiě)PizzaStore類
1 public class PizzaStore { 2 SimplePizzaFactory factory; 3 public PizzaStore(SimplePizzaFactory factory) { 4 this.factory = factory; 5 } 6 public Pizza orderPizza(String type) { 7 Pizza pizza; 8 pizza=factory.createPizza(type); 9 pizza.prepare(); 10 pizza.bake(); 11 pizza.cut(); 12 pizza.box(); 13 return pizza; 14 } 15 //other methods here 16 }簡(jiǎn)單工廠模式
餃子館中的簡(jiǎn)單工廠實(shí)現(xiàn)
?水餃要實(shí)現(xiàn)的接口
1 /// <summary> 2 /// 水餃要實(shí)現(xiàn)的接口 3 /// </summary> 4 public interface IDumpling 5 { 6 string DumplingName { get; } 7 /// <summary> 8 /// 水餃的準(zhǔn)備階段方法 9 /// </summary> 10 void Prepare(); 11 /// <summary> 12 /// 煮 13 /// </summary> 14 void Boild(); 15 /// <summary> 16 /// 展示訂單 17 /// </summary> 18 void Show(); 19 }具體的水餃類
1 /// <summary> 2 /// 豬肉大蔥水餃類 3 /// </summary> 4 public class PorkAndScallionDumpling:IDumpling 5 { 6 7 public string DumplingName 8 { 9 get { return "豬肉大蔥水餃"; } 10 } 11 12 public void Prepare() 13 { 14 Console.WriteLine("準(zhǔn)備豬肉茴香水餃25個(gè)"); 15 } 16 17 public void Boild() 18 { 19 Console.WriteLine("正在煮......請(qǐng)稍等....."); 20 } 21 22 public void Show() 23 { 24 Console.WriteLine("您的{0},準(zhǔn)備好了。", this.DumplingName); 25 } 26 } 1 /// <summary> 2 /// 豬肉茴香水餃類 3 /// </summary> 4 public class PorkAndFennelDumpling : IDumpling 5 { 6 public string DumplingName 7 { 8 get { return "豬肉茴香水餃"; } 9 } 10 11 public void Prepare() 12 { 13 Console.WriteLine("準(zhǔn)備豬肉茴香水餃25個(gè)"); 14 } 15 16 public void Boild() 17 { 18 Console.WriteLine("正在煮......請(qǐng)稍等....."); 19 } 20 21 public void Show() 22 { 23 Console.WriteLine("您的{0},準(zhǔn)備好了。", this.DumplingName); 24 } 25 }工廠類
1 /// <summary> 2 /// 水餃的生產(chǎn)工廠 3 /// </summary> 4 public static class SimpleDumplingFactory 5 { 6 public static IDumpling CreateDumpling(string dumplingName) 7 { 8 IDumpling dumpling = null; 9 switch (dumplingName) 10 { 11 case "豬肉大蔥": 12 dumpling = new PorkAndScallionDumpling(); 13 break; 14 case "豬肉茴香": 15 dumpling = new PorkAndFennelDumpling(); 16 break; 17 } 18 return dumpling; 19 } 20 }控制臺(tái)代碼
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 IDumpling dumpling = SimpleDumplingFactory.CreateDumpling("豬肉茴香"); 6 dumpling.Prepare(); 7 dumpling.Boild(); 8 dumpling.Show(); 9 Console.Read(); 10 } 11 }結(jié)果
授權(quán)pizza店
我們的pizza店非常成功,許多人都想開(kāi)設(shè)我們的授權(quán)加盟店。為保證質(zhì)量,我們希望他們使用我們經(jīng)過(guò)時(shí)間考驗(yàn)的代碼。
但是,不同地區(qū)的加盟pizza店可能希望供應(yīng)不同口味的pizza。怎么解決這個(gè)問(wèn)題呢?
解決方法之一:建立不同的工廠
1 //建立不同的工廠:如NYPizzaFactory、 ChicagoPizzaFactory、 CaliforniaPizzaFactory,在PizzaStore中包含相應(yīng)工廠的實(shí)例。其代碼類似于: 2 //該pizza店提供紐約風(fēng)味的pizza 3 NYPizzaFactory nyFactory=new NYPizzaFactory();//建立一個(gè)生產(chǎn)紐約風(fēng)味pizza的工廠 4 PizzaStore nyStore=new PizzaStore(nyFactory);//建立一個(gè)pizza店,引用紐約風(fēng)味pizza的工廠 5 nyStore.orderPizza(“Veggie”);//生產(chǎn)的是紐約風(fēng)味的pizza 6 7 //該pizza店提供芝加哥風(fēng)味的pizza 8 ChicagoPizzaFactory chicagoFactory=new ChicagoPizzaFactory(); 9 PizzaStore chicagoStore=new PizzaStore(chicagoFactory); 10 chicagoStore.orderPizza(“Veggie”);抽象工廠模式
這么多工廠,可以再增加抽象層
另一種解決方法-工廠方法模式
思路:改寫(xiě)的PizzaStore,將createPizza()方法放回到PizzaStore,但是聲明為抽象方法,然后,為每一種地方風(fēng)味創(chuàng)建一個(gè)PizzaStore的子類。
改造后的PizzaStore的代碼
1 public abstract class PizzaStore { 2 3 public Pizza orderPizza(String type) { 4 Pizza pizza = createPizza(type);//在PizzaStore內(nèi)調(diào)用自身的一個(gè)方法來(lái)制造pizza,而不是使用一個(gè)factory對(duì)象 5 6 pizza.prepare(); 7 pizza.bake(); 8 pizza.cut(); 9 pizza.box(); 10 return pizza; 11 } 12 abstract Pizza createPizza(String type);//factory對(duì)象成了這里的一個(gè)抽象方法 13 14 }下面我們需要PizzaStore的各種子類(對(duì)應(yīng)不同的地區(qū)風(fēng)味)
讓子類做決定
?
?聲明工廠方法
abstract Pizza createPizza(String type); abstract Product factoryMethod(String type);工廠方法是抽象的,在一個(gè)超類中定義。必須由子類來(lái)實(shí)現(xiàn)。
工廠方法返回一個(gè)產(chǎn)品,該產(chǎn)品通常在其所在類的方法中定義。(如orderPizza())
工廠方法通常提供參數(shù),用以選擇一個(gè)產(chǎn)品的不同品種。
工廠方法將客戶(超類中的方法,如PizzaStore中的orderPizza())與具體的產(chǎn)品相隔離。
?工廠方法怎么工作?
假定張三喜歡紐約風(fēng)味的pizza,李四喜歡芝加哥風(fēng)味的pizza。
需要相應(yīng)Pizza店的實(shí)例
調(diào)用orderPizza()訂購(gòu)想要的pizza品種
createPizza()被調(diào)用,并返回pizza到orderPizza()方法。
盡管不知道是什么pizza,但orderPizza()仍知道對(duì)它進(jìn)行后續(xù)處理。
工廠方法模式中的類
創(chuàng)建者類 The Creator classes
產(chǎn)品類 The Product classes
工廠方法模式的定義
定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪一個(gè)類。工廠方法模式讓一個(gè)類的實(shí)例化延遲到其子類。
工廠方法模式的結(jié)構(gòu)
總結(jié):Factory Method模式
意圖
定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪一個(gè)類。工廠方法模式讓一個(gè)類的實(shí)例化延遲到其子類。
別名
虛擬構(gòu)造器
Factory Method—參與者
Product(document)定義工廠方法所創(chuàng)建對(duì)象的接口。
ConcreteProduct(mydocument)實(shí)現(xiàn)product接口。
Creator(application)聲明工廠方法,可以調(diào)用工廠方法以創(chuàng)建一個(gè)product對(duì)象
ConcreteCreator (MyApplication)重新定義工廠方法,以返回一個(gè)ConcreteProduct實(shí)例
(篇幅有點(diǎn)長(zhǎng),關(guān)于工廠方法模式的實(shí)例就不再列舉了,感興趣的可以自己實(shí)現(xiàn)一下)
?參考書(shū):
《First Head 設(shè)計(jì)模式》
?
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的[Head First设计模式]饺子馆(冬至)中的设计模式——工厂模式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C#线程间通信
- 下一篇: 小菜学习Lucene.Net(更新3.0