使用C# (.NET Core) 實現抽象工廠設計模式 (Abstract Pattern)

solenovex發表於2018-04-10

本文的概念性內容來自深入淺出設計模式一書.

上一篇文章講了簡單工廠和工廠方法設計模式 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
    {
    }
}
View Code

 

原料工廠介面:

namespace AbstractFactoryPattern.Abstractions
{
    public interface IPizzaIngredientFactory
    {
        IDough CreateDough();
        ICheese CreateCheese();
        IClams CreateClams();
        ISauce CreateSauce();
    }
}
View Code

 

披薩店抽象類:

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);
    }
}
View Code

 

披薩抽象類:

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......");
        }
    }
}
View Code

 

具體原料:

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";
    }
}
View Code

 

具體披薩:

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}");
        }
    }
}
View Code

 

各地原料工廠:

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();
        }
    }
}
View Code

 

各地披薩店:

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;
        }
    }
}
View Code

 

測試執行:

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();
        }
    }
}
View Code

 

Ok.

相關文章