zoukankan      html  css  js  c++  java
  • 使用C# (.NET Core) 实现抽象工厂设计模式 (Abstract Pattern)

    本文的概念性内容来自深入浅出设计模式一书.

    上一篇文章讲了简单工厂和工厂方法设计模式 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.

  • 相关阅读:
    OOP侧边分享按钮
    表格基础操作
    行为型模式之自定义语言的实现(解释器模式)
    行为型模式之请求发送者与接收者解耦(命令模式)
    行为型模式之请求的链式处理(职责链模式)
    Http、Socket、WebSocket之间联系与区别
    日期时间工具类DateTimeUtil(基于Java8的LocalDateTime)
    结构型模式之代理模式
    Java8 函数式接口@FunctionalInterface的使用说明
    结构型模式之实现对象的复用(享元模式)
  • 原文地址:https://www.cnblogs.com/cgzl/p/8776868.html
Copyright © 2011-2022 走看看