前言:按照慣例我以Head First設計模式的工廠模式例子開始編碼學習。並由簡單工廠,工廠模式,抽象工廠模式依次演變,歸納他們的相同與不同。
話說Head First認為簡單工廠並不是設計模式,而是一種編程習慣,但並不妨礙我們使用它,接下來我們對工廠模式一探究竟。
1、披薩店例子
首先我們要開一個披薩店,對於業務不復雜的情況下我們可以快速的開發出一個披薩店以及訂購披薩的邏輯
public Pizza OrderPizza() { Pizza pizza = new Pizza(); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } } public class Pizza { //准備 public void Prepare() { } //烘烤 public void Bake() { } //切片 public void Cut() { } //裝盒 public void Box() { } }
如果我們有更多的披薩種類可能需要將Pizza定義成抽象類 在訂單里面根據訂購的披薩種類返回不同的披薩,我們對披薩進行抽象並改造Order。
public class PizzaStore { public Pizza OrderPizza(string type) { Pizza pizza=null; if (type == "cheese") { pizza = new CheesePizza(); } else if (type == "viggie") { pizza = new VeggiePizza(); } //else if ...... pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } } public abstract class Pizza { //准備 public void Prepare() { } //烘烤 public void Bake() { } //切片 public void Cut() { } //裝盒 public void Box() { } } //奶酪披薩 public class CheesePizza : Pizza { } //素食披薩 public class VeggiePizza : Pizza { }
到這里我們可能想到了,如果增加披薩種類或者移除披薩那么我們將對披薩店進行修改。
設計原則對擴展開放,對修改關閉。我們需要將創建披薩的變化封裝起來。對此弄出來一個專門創建披薩的“工廠“類。
並采用靜態,這樣就不需要實例化對象,也遵循了不對實現編程原則。
public class PizzaStore { public Pizza OrderPizza(string type) { Pizza pizza = SimplePizzaFactory.CreatePizza(type); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } } public static class SimplePizzaFactory { public static Pizza CreatePizza(string type) { Pizza pizza = null; if (type == "cheese") { pizza = new CheesePizza(); } else if (type == "viggie") { pizza = new VeggiePizza(); } return pizza; } }
這樣將創建披薩簡單的封裝起來即是簡單工廠(靜態工廠),簡單工廠也可以不用靜態類,但簡單工廠並不是一種專門的設計模式(有時候可能會混淆,認為這即是”工廠模式“),更像是我們平時編程都會做的一種習慣。我們將改動封裝在一個局部當有變化的時候只需要修改這個工廠類。
2、更多的披薩店
現在我們要開更多的披薩店,例如美國風格披薩店(USSytlePizzaStore)、中國風格披薩店(CHNSytlePizzaStore)。
我們可以采用簡單工廠模式,創建兩個不同風格的披薩工廠,然后創建兩個不同風格的披薩店,不同風格的披薩店使用對應的披薩工廠來獲取。
但是我們此時的變化點是披薩店。我們希望披薩店的結構或者流程是按照一定規則的,只是不同風格的披薩。此時我們有更好的解決辦法:工廠模式。
接下來我們看如何實現
public abstract class PizzaStore { public Pizza OrderPizza(string type) { Pizza pizza= CreatePizza(type); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } public abstract Pizza CreatePizza(string type); } public class USSytlePizzaStore : PizzaStore { public override Pizza CreatePizza(string type) { Pizza pizza = null; if (type == "cheese") { pizza = new USStyleCheesePizza(); } else if (type == "viggie") { pizza = new USStyleVeggiePizza(); } return pizza; } } public class CHNSytlePizzaStore : PizzaStore { public override Pizza CreatePizza(string type) { Pizza pizza = null; if (type == "cheese") { pizza = new CHNStyleCheesePizza(); } else if (type == "viggie") { pizza = new CHNStyleVeggiePizza(); } return pizza; } } //US奶酪披薩 public class USStyleCheesePizza : Pizza { } //US素食披薩 public class USStyleVeggiePizza : Pizza { } //CHN奶酪披薩 public class CHNStyleCheesePizza : Pizza { } //CHN素食披薩 public class CHNStyleVeggiePizza : Pizza { }
由實現我們可以看到我們將PizzaStore修改成抽象類,不同的披薩店繼承抽象類返回自己不同風格的披薩。
這樣設計后當增加產品,我們也只是在具體的子類披薩店中修改其中的披薩創建,不會影響披薩店本身流程和其他披薩店的實現。
工廠方法模式:定義了一個創建對象的接口,由子類決定要實例化的類是哪一個,工廠方法讓類把實例化推遲到子類。
工廠方法與簡單工廠的區別:工廠方法的子類看起來很像簡單工廠。簡單工廠把全部的事情在一個地方處理完成,而工廠方法卻是創建一個框架,讓子類決定如何實現。
3、披薩的不同原料
不同風格的披薩店有不同風格的披薩,而這些披薩的不同風格是來自不同原料造成,所以不同風格的披薩變化的部分是材料。
我們先建造原料和原料工廠,以中國披薩原料工廠為例
//披薩原料工廠接口 public interface PizzaIngredientFactory { public Veggie CreateVeggie(); public Cheese CreateCheese(); } //具體工廠實現 public class CNHPizzaIngredientFactory : PizzaIngredientFactory { public Cheese CreateCheese() { return new CHNCheese(); } public Veggie CreateVeggie() { return new CHNVeggie(); } } public abstract class Veggie { } public class USVeggie : Veggie { } public class CHNVeggie : Veggie { } public abstract class Cheese { } public class USCheese : Cheese { } public class CHNCheese : Cheese { }
然后重做Pizza
public abstract class Pizza { public String Name; Veggie veggie; Cheese cheese; //准備 public abstract void Prepare(); //烘烤 public void Bake() { } //切片 public void Cut() { } //裝盒 public void Box() { } }
加入了原料的抽象 Veggie 和 Cheese,同時我們讓Prepare變成抽象方法,讓他的具體子類決定用什么材制造不同風格的披薩。接着我們重做子類,以CheesePizza為例
//奶酪披薩 public class CheesePizza : Pizza { PizzaIngredientFactory IngredientFactory; public CheesePizza(PizzaIngredientFactory IngredientFactory) { this.IngredientFactory = IngredientFactory; } public override void Prepare() { IngredientFactory.CreateCheese(); } }
修改中國披薩店
public class CHNSytlePizzaStore : PizzaStore { public override Pizza CreatePizza(string type) { Pizza pizza = null; //創建中國原材料工廠 CNHPizzaIngredientFactory ingredientFactory = new CNHPizzaIngredientFactory(); if (type == "cheese") { pizza = new CheesePizza(ingredientFactory); } else if (type == "viggie") { pizza = new VeggiePizza(ingredientFactory); } return pizza; } }
通過這一系列的改造我們引入了新類型的工廠,也就是所謂的抽象工廠,抽象工廠用來創造原料。
利用抽象工廠我們代碼將從實際工廠解耦,這樣如果我們的工廠需要擴展那么我們則可在子類中進行修改擴展。
工廠方法與抽象工廠的異同優缺點
相同:都是用來創建對象。
不同:工廠方法使用的是繼承,抽象工廠使用的是組合。
優點:工廠方法只負責從具體類型中解耦,抽象工廠適合將一群相關的產品集合起來。
缺點:抽象工廠擴展接口需要修改每個子類。