zoukankan      html  css  js  c++  java
  • C#设计模式之6:抽象工厂模式

    前面分析了简单工厂模式和工厂方法模式,接着来看一下抽象工厂模式,他与工厂方法模式有一些相似的地方,也有不同的地方。

    先来看一个不用工厂方法模式实现的订购披萨的代码:

    对象依赖的问题:当你直接实例化一个对象时,就是在依赖他的具体类。接着上面的例子,如果在一个PizzaStore里面直接创建很多对象时,他们的依赖关系是这样的:

    这里引出一个概念:依赖倒置。很清楚的代码里减少具体类的依赖是一件好事。依赖倒置的定义是:要依赖抽象,不要依赖实现。这个原则说说明了:不能让高层组建依赖底层组件,而且,不管是高层组件还是底层组件,他们都要依赖抽象。所谓的高层组件,是由其它底层组件定义其行为的类。例如。PizzaStore是个高层组件,因为他的行为是由比萨定义的:PizzaStore创建所有不同的比萨对象,准备、烘烤、切片、装盒;而披萨本身属于低层组件。

    原则的应用

    上图展示的问题在于,它依赖每个比萨类型,因为它是在自己的orderPizza方法中,实例化这些具体类型的。虽然我们已经创建了一个抽象,就是Pizza,但是我们仍然在代码中,实际的创建了具体的pizza,所以,这个抽象没有什么影响力。如何在OrderPizza方法中,将这些实例化对象的代码独立出来,我们都知道,工厂方法刚好能排上用场。所以,应用工厂方法之后。类图看起来就像这:

     在应用了工厂方法模式之后(参考工厂方法模式),你将注意到,高层组件和底层组件都依赖了抽象(Pizza),要遵循依赖倒置原则,工厂方法并非唯一的技巧,但是,确实最有威力的技巧之一。

    如何更好的遵循依赖倒置原则

    变量不可以持有具体类的引用。如果使用new,就会持有具体类的引用,就会对具体的类产生依赖。可以改用工厂来避免这种做法。

    不要让类派生自具体的类。如果派生自具体类,那你就会依赖具体类。请派生自一个抽象。

    不要覆盖基类中已经实现的方法。基类中已实现的方法,应该由所有的子类共享。

    上面三点只是说尽量去做到,并不是一定要做到。

    抽象工厂模式

    这个模式涉及的类有点儿多。。。所以决定线上UML类图,然后根据类图来一步一步的说明:

    实际上单就拿抽象工厂来说,不是太难说明这个模式的含义,但是抽象工厂一般是和工厂方法模式配合使用的:

    看图:首先,NYPizzaStore还是继承自PizzaStore

    public abstract   class PizzaStore
        {
            public Pizza OrderPizza(string pizzaType)
            {
                Pizza pizza = CreatePizza(pizzaType);
                pizza.Prepare();
                pizza.Bake();
                pizza.Cut();
                pizza.Box();
                return pizza;
            }
            public abstract Pizza CreatePizza(string pizzaType);
        }
    View Code

     PizzaStroe依赖一个抽象的Pizza,而NYPizzaStore继承了PizzaStore,覆写了CreatePIzza:

     public class NyPizzaStore:PizzaStore
        {
            public override Pizza CreatePizza(string pizzaType)
            {
                Pizza pizza=null;
                var indigredientFactory=new NyPizzaIngredientFactory();
                if (pizzaType=="Cheese")
                {
                    pizza = new CheesePizza(indigredientFactory);
                }
                else if (pizzaType=="Clam")
                {
                    pizza=new ClamPizza(indigredientFactory);
                }
                return pizza;
            }
        }
    View Code

    实现的这个NYPizzaStore的耦合比较高,因为他在俩面new了三个对象:一个抽象工厂NyPizzaIngredientFactory,两个具体的披萨CheesePizza和ClamPizza。但是因为这个部分应该是不易变的部分,以后也不会进行修改了(如果不是这样的话那这个类还得继续抽象不是么?)此外,NYPizzaStore还针对一个Pizza抽象进行了编程,Pizza的代码如下:

    public abstract class Pizza
        {
            public string Name { get; set; }
            public Dough Dough { get; set; }
            public Sauce Sauce { get; set; }
            public Veggie[] Veggies { get; set; }
            public Repperoni Repperoni { get; set; }
            public Clams Clams { get; set; }
            public Cheese Cheese { get; set; }
            public abstract void Prepare();
    
           public void Bake()
            {
                Console.WriteLine("bake...");
            }
    
           public void Cut()
            {
                Console.WriteLine("Cut...");
            }
    
           public void Box()
            {
                Console.WriteLine("Box...");
            }
    
            public override string ToString()
            {
                return this.Name;
            }
        }
    View Code

    Pizza里面依赖了很多的抽象,也就是说Pizza是针对抽象在编程,具体是一个什么披萨,应该交给Pizza的子类来进行详细的描述,此外,看到Pizza里面还有一个抽象的Prepare方法,由此判断出来Pizza类也实现了工厂方法模式。看一下Pizza的具体实现类(其中一个):

     public class CheesePizza : Pizza
        {
            private IPizzaIngredientFactory _factory;
    
            public CheesePizza(IPizzaIngredientFactory factory)
            {
                _factory = factory;
            }
            public override void Prepare()
            {
                Console.WriteLine($"Preparing {Name}");
                Dough = _factory.CreateDough();
                Sauce = _factory.CreateSauce();
                Cheese = _factory.CreateCheese();
            }
        }
    View Code

    CheesePizaa针对一个抽象的IPizzaIngredientFactory进行编程,这种利用组合的方式避免了继承带来的静态的编译所造成的不便,使得代码可以在运行时体现出更灵活和可扩展的特性。IPizzaIngredientFactory是一个抽象工厂的接口,抽象工厂又叫做原料工厂,就是说生产产品所需要的各种原料都可以从抽象工厂来获取:

     public class NyPizzaIngredientFactory : IPizzaIngredientFactory
        {
            public Dough CreateDough()
            {
                return new ThinCruseDough();
            }
    
            public Sauce CreateSauce()
            {
                return new MarinaraSauce();
            }
    
            public Cheese CreateCheese()
            {
                return new ReggianoChesse();
            }
    
            public Veggie[] CreateVeggies()
            {
                return new Veggie[]
                {
                   new Garlic(), new Mushroom(), new Onion(), new RedPepper(),
                };
            }
    
            public Repperoni CreateRepperoni()
            {
                return new SlicedRepperoni();
            }
    
            public Clams CreateClams()
            {
                return new FreshClams();
            }
        }
    View Code

    下面给出抽象工厂的定义:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。在这个示例中,这个接口指的就是IPizzaInfredientFactory。

    最重要的结论是,抽象工厂并不是单独使用的,而是在工厂方法模式中进行扩展使用的。有的时候,我们要创建一些相关联的或依赖的一族对象,这个时候可以把创建这些对象用抽象工厂来实现,而不是用工厂方法模式来实现,如果用工厂方法模式来实现,一个抽象产品对应一个抽象工厂,那样的情况叫做“类型爆炸”。

    最最重要的是:工厂方法模式遵循控制反转的设计原则,并在此基础上定义了一个框架,在框架中,如果又必要的话,将抽象工厂的逻辑放到这个框架中的适当位置上。所以,工厂方法模式和抽象工厂模式一般是配合使用的。抽象工厂的作用就是防止"类型爆炸”。

    要点:

    所有工厂都是用来封装对象的创建。

    简单工厂提供给我们更多的是一种想法,让我们萌生了将系统做一些分隔的想法,比如将对象的使用和实现进行解耦,等等。

    工厂方法使用的是继承和多态,吧对象的创建委托给子类。

    抽象工厂使用的是对象的组合。对象的创建被实现在工厂接口所暴露的方法中来,比如Pizza类就实现工厂方法模式,里面的Prepare就是一个工厂方法。

    所有工厂模式都是通过减少应用程序和类之间的依赖促进耦合。

    依赖倒置原则,指导我们要尽量避免依赖具体,而要依赖抽象。

    工厂是很有威力的技巧,帮我们尽量针对接口编程,而不是针对具体。

  • 相关阅读:
    springmvc视图解析
    mysql外键是多个id组成的字符串,查询方法
    mysql服务无法启动(1067错误)时数据备份的经验
    springboot(5) freemarker
    springboot(4) Log之Logbak
    springboot(3) junit单元测试
    集合类基础知识
    springboot(2) 数据库操作
    springboot(1)
    linux命令
  • 原文地址:https://www.cnblogs.com/pangjianxin/p/7993313.html
Copyright © 2011-2022 走看看