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