本文的概念性內容來自深入淺出設計模式一書.
上一篇文章講了簡單工廠和工廠方法設計模式 http://www.cnblogs.com/cgzl/p/8760250.html, 使用的是披薩店的例子.
文將繼續使用這個例子, 這裡要用到抽象工廠.
披薩店的需求變更
現在披薩店在各地授權了很多連鎖分店, 但是有的分店偷工減料, 使用劣質原料代替標準原料.
披薩店老闆現在就是想解決這個問題.
原料的一致性問題
首先如何保證原料的質量問題? 可以建立一個工廠生產原料, 然後把原料分發到各地的授權店.
然後還有一個原料的一致性問題, 例如紐約的番茄醬和芝加哥的番茄醬可能有點不同, 所以它們各自需要一套原料.
也就是說各地的披薩是使用相同的原料, 但是每種原料在各地可能會存在差異(不同的實現).
這就是紐約, 芝加哥和加州各自的原料家族.
建立原料工廠
接下來就是建立原料工廠, 這些工廠將負責為各自的家族(地點)建立原料.
首先是工廠的介面:
然後我們要做下面這些內容:
- 為每個地區建立一個工廠 (實現PizzaIngredientFactory介面及其方法)
- 實現一些原料的類, 它們可以唄工廠使用, 其中某些原料可以跨地區共享
- 最後我們把上面這些整合到PizzaStore裡面.
紐約的原料工廠:
就是實現介面, 返回本地需要的原料而已.
修改Pizza抽象類:
這裡我們把Prepare()方法(準備原料)改成抽象的了, 其它的保持不變.
接下來需要為各地建立不同風格的披薩了. 現在各地披薩店的原料都是從工廠送來的, 就不能使用劣質原料代替了.
之前使用工廠方法模式時, 我們為每個地點建立了不同風格的披薩, 例如 NYCheesePizza, ChicagoCheesePizza. 你可以看一下這兩個類, 它們裡面只有原料部分(都是同樣的原料, 但是各地風格不同)是不同的.
所以實際上, 我們不需要為每個地點建立不同風格的披薩, 原料工廠將會替我們處理各地風格披薩原料不同這種情況.
例如乳酪披薩只需要一個類就可以:
為了建立乳酪披薩, 在其建構函式裡面傳入原料工廠為它提供原料即可.
在prepare()方法裡面準備的原料都是工廠來提供的.
使用哪些地區/風格的原料由工廠決定, 披薩類本身並不關心, 它只需知道怎麼製作披薩就行.
這樣披薩類和各地區的原材料就解耦了.
綜上, 就是一句話:
原料由工廠提供.
可以再看看另外一個披薩的例子:
修改各地的披薩店
紐約的披薩店現在和紐約的原料工廠組合在一起, 這樣它就可以產出紐約風格的披薩了.
在建立披薩的時候把原料工廠傳進去為披薩提供原料.
到目前位置, 我們做了什麼?
我們提供了一種可以為披薩提供一族原料的工廠, 這個工廠就叫做抽象工廠.
抽象工廠為建立某一家族的產品提供介面(interface), 針對這個介面程式設計, 就可以實現從具體生產產品的工廠解耦.
這樣做就允許我們為不同的上下文使用不同實現的工廠了.
因為我們的程式碼是從實際產品解耦的, 我們就可以通過替換工廠來取得不同風格的產品了.
梳理一下整個流程
1. 建立紐約的披薩店:
2. 下訂單買披薩
3. orderPizza方法呼叫建立披薩的方法:
到這, 程式碼都沒有變化.
4.建立披薩的時候, 使用原料工廠:
5. 披薩的準備工序裡是由工廠來提供原料:
6. 按照其他工序加工並返回披薩.
抽象工廠定義
抽象工廠設計模式提供了一個介面, 這個介面可以建立一族相關或依賴的物件而無需指明它們具體的類.
下面是類圖:
對應披薩店的圖:
工廠方法和抽象工廠的比較
工廠方法是通過繼承來實現建立物件工作的. 而抽象工廠則是通過組合的方法.
工廠方法是讓子類來建立物件, 客戶只需要知道抽象類, 子類做具體的實現, 解耦.
抽象工廠提供了一個可以建立一族產品的抽象類, 這個類的實現類/子類決定產品是如何產出的, 也是解耦.
抽象工廠的優點是: 可以建立一族相關的產品. 缺點是它的介面比較大, 如果新增產品了需要改介面.
而工廠方法只負責生產一個產品.
抽象工廠也經常使用工廠方法來實現具體的工廠.
而工廠方法也經常使用抽象的創造者, 它來使用子類創造出的具體產品.
工廠方法:
抽象工廠:
總結
C#/.NET Core程式碼實現
原料介面:
namespace AbstractFactoryPattern.Abstractions { public interface IGredient { string Name { get; } } } namespace AbstractFactoryPattern.Abstractions { public interface ICheese: IGredient { } } namespace AbstractFactoryPattern.Abstractions { public interface IClams: IGredient { } } namespace AbstractFactoryPattern.Abstractions { public interface IDough: IGredient { } } namespace AbstractFactoryPattern.Abstractions { public interface ISauce: IGredient { } }
原料工廠介面:
namespace AbstractFactoryPattern.Abstractions { public interface IPizzaIngredientFactory { IDough CreateDough(); ICheese CreateCheese(); IClams CreateClams(); ISauce CreateSauce(); } }
披薩店抽象類:
namespace AbstractFactoryPattern.Abstractions { public abstract class PizzaStore { public Pizza OrderPizza(string type) { var pizza = CreatePizza(type); pizza.Prepare(); pizza.Bake(); pizza.Cut(); pizza.Box(); return pizza; } protected abstract Pizza CreatePizza(string type); } }
披薩抽象類:
using System; namespace AbstractFactoryPattern.Abstractions { public abstract class Pizza { public string Name { get; set; } public IDough Dough { get; protected set; } public ISauce Sauce { get; protected set; } public ICheese Cheese { get; protected set; } public IClams Clams { get; protected set; } public abstract void Prepare(); public void Bake() { Console.WriteLine("Bake for 25 minutes"); } public void Cut() { Console.WriteLine("Cutting the pizza into diagnol slices"); } public void Box() { Console.WriteLine("Placing pizza in official PizzaStore box......"); } } }
具體原料:
using AbstractFactoryPattern.Abstractions; namespace AbstractFactoryPattern.Ingredients { public class FreshClams : IClams { public string Name { get; } = "Fresh Clams"; } } using AbstractFactoryPattern.Abstractions; namespace AbstractFactoryPattern.Ingredients { public class FrozenClams: IClams { public string Name { get; } = "Frozen Clams"; } } using AbstractFactoryPattern.Abstractions; namespace AbstractFactoryPattern.Ingredients { public class MarinaraSauce: ISauce { public string Name { get; } = "Marinara Sauce"; } } using AbstractFactoryPattern.Abstractions; namespace AbstractFactoryPattern.Ingredients { public class MozzarellaCheese: ICheese { public string Name { get; } = "Mozzarella Cheese"; } } using AbstractFactoryPattern.Abstractions; namespace AbstractFactoryPattern.Ingredients { public class PlumTomatoSauce : ISauce { public string Name { get; } = "Plum Tomato Sauce"; } } using AbstractFactoryPattern.Abstractions; namespace AbstractFactoryPattern.Ingredients { public class ReggianoCheese : ICheese { public string Name { get; } = "Reggiano Cheese"; } } using AbstractFactoryPattern.Abstractions; namespace AbstractFactoryPattern.Ingredients { public class ThickCrustDough: IDough { public string Name { get; } = "Thick Crust Dough"; } } using AbstractFactoryPattern.Abstractions; namespace AbstractFactoryPattern.Ingredients { public class ThinCrustDough: IDough { public string Name { get; } = "Thin Crust Dough"; } }
具體披薩:
using System; using AbstractFactoryPattern.Abstractions; namespace AbstractFactoryPattern.ConcreteProducts { public class CheesePizza: Pizza { private readonly IPizzaIngredientFactory _pizzaIngredientFactory; public CheesePizza(IPizzaIngredientFactory pizzaIngredientFactory) { _pizzaIngredientFactory = pizzaIngredientFactory; } public override void Prepare() { Console.WriteLine($"Preparing: {Name}"); Dough = _pizzaIngredientFactory.CreateDough(); Sauce = _pizzaIngredientFactory.CreateSauce(); Clams = _pizzaIngredientFactory.CreateClams(); Cheese = _pizzaIngredientFactory.CreateCheese(); Console.WriteLine($" {Dough.Name}"); Console.WriteLine($" {Sauce.Name}"); Console.WriteLine($" {Clams.Name}"); Console.WriteLine($" {Cheese.Name}"); } } } using System; using AbstractFactoryPattern.Abstractions; namespace AbstractFactoryPattern.ConcreteProducts { public class ClamsPizza : Pizza { private readonly IPizzaIngredientFactory _pizzaIngredientFactory; public ClamsPizza(IPizzaIngredientFactory pizzaIngredientFactory) { _pizzaIngredientFactory = pizzaIngredientFactory; } public override void Prepare() { Console.WriteLine($"Preparing: {Name}"); Dough = _pizzaIngredientFactory.CreateDough(); Sauce = _pizzaIngredientFactory.CreateSauce(); Clams = _pizzaIngredientFactory.CreateClams(); Console.WriteLine($" {Dough.Name}"); Console.WriteLine($" {Sauce.Name}"); Console.WriteLine($" {Clams.Name}"); } } }
各地原料工廠:
using AbstractFactoryPattern.Abstractions; using AbstractFactoryPattern.Ingredients; namespace AbstractFactoryPattern.ConcreteFactories { public class ChicagoPizzaIngredientFactory: IPizzaIngredientFactory { public IDough CreateDough() { return new ThinCrustDough(); } public ICheese CreateCheese() { return new ReggianoCheese(); } public IClams CreateClams() { return new FrozenClams(); } public ISauce CreateSauce() { return new PlumTomatoSauce(); } } } using AbstractFactoryPattern.Abstractions; using AbstractFactoryPattern.Ingredients; namespace AbstractFactoryPattern.ConcreteFactories { public class NewYorkPizzaIngredientFactory: IPizzaIngredientFactory { public IDough CreateDough() { return new ThickCrustDough(); } public ICheese CreateCheese() { return new MozzarellaCheese(); } public IClams CreateClams() { return new FreshClams(); } public ISauce CreateSauce() { return new MarinaraSauce(); } } }
各地披薩店:
using AbstractFactoryPattern.Abstractions; using AbstractFactoryPattern.ConcreteFactories; using AbstractFactoryPattern.ConcreteProducts; namespace AbstractFactoryPattern.Clients { public class ChicagoPizzaStore : PizzaStore { protected override Pizza CreatePizza(string type) { var factory = new ChicagoPizzaIngredientFactory(); Pizza pizza = null; switch (type) { case "cheese": pizza = new CheesePizza(factory); pizza.Name = "Chicago Cheese Pizza"; break; case "clams": pizza = new ClamsPizza(factory); pizza.Name = "Chicago Clams Pizza"; break; } return pizza; } } } using AbstractFactoryPattern.Abstractions; using AbstractFactoryPattern.ConcreteFactories; using AbstractFactoryPattern.ConcreteProducts; namespace AbstractFactoryPattern.Clients { public class NewYorkPizzaStore : PizzaStore { protected override Pizza CreatePizza(string type) { var factory = new NewYorkPizzaIngredientFactory(); Pizza pizza = null; switch (type) { case "cheese": pizza = new CheesePizza(factory); pizza.Name = "New York Cheese Pizza"; break; case "clams": pizza = new ClamsPizza(factory); pizza.Name = "New York Clams Pizza"; break; } return pizza; } } }
測試執行:
using System; using AbstractFactoryPattern.Clients; namespace AbstractFactoryPattern { class Program { static void Main(string[] args) { var newYorkPizzaStore = new NewYorkPizzaStore(); newYorkPizzaStore.OrderPizza("cheese"); Console.WriteLine("-----------------------------------------------------------"); var chicagoYorkPizzaStore = new ChicagoPizzaStore(); chicagoYorkPizzaStore.OrderPizza("cheese"); Console.ReadKey(); } } }
Ok.