zoukankan      html  css  js  c++  java
  • Head First 设计模式之工厂模式(Factory Pattern)

    前言:

         除了使用new操作符之外,还有更多制造对象的方法。你将了解到实例化这个活动不应该总是公开的进行,也会意识到初始化会造成“耦合”的问题。工厂模式将会从复杂的依赖中帮你脱困。

    1.   简单的工厂

    当看到“new”,就会想到“具体”,的确也是在实例化一个具体的类,而不是接口。代码绑着具体的类导致代码更脆弱,更缺乏弹性。当有一群相关的具体类时,通常会有如下代码:

    Duck duck;
    If(picnic)
     duck=new MallardDuck();
    else if(hunting)
     duck=new DecoyDuck();
    else if(inBathTub)
     duck=new RubberDuck();

    这样的代码一旦有变化或者扩展,就必须重新修改此段代码进行检查和修改,这样的修改容易导致系统的维护和更新更难,也更容易犯错。

    针对接口编程,可以隔离掉以后系统可能发生的一大堆改变,因为针对接口而写,可以通过多态,与任何新类实现该接口。当代码使用具体类时,一旦加入新的一些具体类就必须改变代码。这就违背了“对修改关闭”的原则。为了解决这样的问题,我们可以通过“找出变化,隔离并封装变化“的方式来解决。

    现实场景:

    披萨店生产披萨,当需要生产更多类型的披萨的时候,压力来自于如何增加更多的披萨类型。

     

    public class Pizza
        {
            Pizza OrderPizza(stringpizzaType)
            {
                Pizza pizza;
                if (pizzaType.Equals("cheese"))
                    pizza = newCheesePizza();
                else if(pizzaType.Equals("greek"))
                    pizza = newGreekPizza();
                else if(pizzaType.Equals("pepperoni"))
                    pizza = newPepperoniPizza();
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
                return pizza;
            }
        }

    如同开始是讲的那样,要新增新的pizza,就需要修改这段代码,修改如下:

     

    public class Pizza
        {
            Pizza OrderPizza(stringpizzaType)
            {
                Pizza pizza;
                if(pizzaType.Equals("cheese"))
                    pizza = newCheesePizza();
                else if(pizzaType.Equals("greek"))
                    pizza = newGreekPizza();
                else if(pizzaType.Equals("pepperoni"))
                    pizza = newPepperoniPizza();
                else if(pizzaType.Equals("clam"))  //新增的pizza类型
                    pizza = newCalmPizza();
                else if(pizzaType.Equals("veggie"))//新增的pizza类型
                    pizza = newVeggiePizza();
     
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
                return pizza;
            }
        }
    


    根据我们上边提到的“将变化抽离并封装“的原则,我们可以将创建pizza实例这一块给抽离出来,因为这块后边可能会新增一些别的pizza类型,由一个对象来专职创建pizza对象。我们称这个新对象为”工厂“。代码如下:

     

    public class SimplePizzaFactory
        {
            public PizzaCreatePizza(string pizzaType)
            {
                Pizza pizza = null;
                if(pizzaType.Equals("cheese"))
                    pizza = newCheesePizza();
                else if (pizzaType.Equals("pepperoni"))
                    pizza = newPepperoniPizza();
                else if(pizzaType.Equals("clam"))
                    pizza = newCalmPizza();
                else if(pizzaType.Equals("veggie"))
                    pizza = newVeggiePizza();
                return pizza;
            }
        }
    


    这样做的好处在于,把创建pizza对象的方法包装成一个类,当以后实现改变时,只需要修改这个类即可。与此同时我们还可以把生成其他类的方法也放在这个简单的工厂中。

    这样我们生成pizza类的代码就变成如下的样子:

     

    public class Pizza
        {
            Pizza OrderPizza(stringpizzaType)
            {
                Pizza pizza;
                SimplePizzaFactorysimplePizzaFactory = new SimplePizzaFactory();//生成pizza
                pizza =simplePizzaFactory.CreatePizza(pizzaType);
                pizza.prepare();
                pizza.bake();
                pizza.cut();
                pizza.box();
                return pizza;
            }
        }


    经过这样一系列的修改,我们的Pizza的类图就变成如下的样子:

    虽然我们一直在说简单的工厂,但事实上简单工厂并不是一个设计模式,更像是一种编程习惯。这里讲简单的工厂,主要是为了引出下面的两个重量级的模式,它们都是工厂。

    2.   工厂方法

    在1中,我们通过简单的工厂解决了生产不同披萨的问题,但是,如果有新的加盟店加盟进来,如何解决不同加盟店的区域差异、质量问题呢?或许,我们可以像1中那样利用简单的工厂,对应不同地区的加盟店创建不同的工厂。

    这样做导致的另一个问题就是,不同的加盟店披萨的制作流程、方法可能不同,如何才能把加盟店和创建披萨捆绑在一起的同时又保持一定的弹性?

    我们可以把CreatePizza()方法放回到PizzaStore中,但是要把它设置为抽象方法,然后为每一个区域加盟店创建一个PizzaStore的子类。

    如下所示:

     

    public abstract class PizzaStore
        {
            public PizzaOrderPizza(string pizzaType)
            {
                Pizza pizza =CreatePizza(pizzaType);
                pizza.Prepare();
                pizza.Bake();
                pizza.Cut();
                pizza.Box();
                return pizza;
            }
     
            public abstract PizzaCreatePizza(string pizzaType);//把工厂对象移到该方法中,该方法为抽象方法
       }

    现在我们有了PizzaStore超类,让各个不同地域的加盟店继承此超类即可。具体的类图如下:

    2.1  声明一个工厂方法

    原本是由一个对象复制所有具体类的实例化,现在通过对PizzaStore做一些转变,变成由一群子类负责实例化。

    工厂方法用来处理对象的创建,并将这样的行为封装在子类中。这样客户程序中关于超类的代码就和子类对象创建代码解耦。

    2.2 具体代码实现

    2.2.1 定义抽象的PizzaStore类,并抽象出工厂方法

     

    public abstract class PizzaStore
        {
           public Pizza OrderPizza(stringtype)
           {
               Pizza pizza = CreatePizza(type);
               pizza.Prepare();
               pizza.Bake();
               pizza.Cut();
               pizza.Box();
     
               return pizza;
           }
          public abstract Pizza CreatePizza(stringtype);//抽象出创建Pizza的工厂方法,由子类实现该方法并创建具体的Pizza
        }
    2.2.2 实现具体的PizzaStore类,让子类做决定

     

    public class MYPizzaStore:PizzaStore
        {
            public  override Pizza CreatePizza(string type)
            {
                Pizza pizza=null;
                switch(type)
                {
                    case "cheese":
                        pizza = new NYStyleCheesePizza();
                        break;
                    case "veggie":
                        pizza=new NYStyleVeggiePizza();
                        break;
                    case "clam":
                        pizza=new NYStyleClamPizza();
                        break;
                    case "pepperoni":
                        pizza=new NYStylePepperoniPizza();
                        break;
                }
                return pizza;
            }
        }
    2.2.3抽象Pizza类,并实现具体的Pizza类
    2.2.3.1 抽象Pizza类
    public abstract  class Pizza
        {
           public string name;
           public string dough;
           public string sauce;     
           public ArrayList toppings = newArrayList();
           public void Prepare()
           {
                System.Console.WriteLine("Preparing" + name);
                System.Console.WriteLine("Tossingdough...");
                System.Console.WriteLine("Addingsauce..");
                System.Console.WriteLine("Addingtoppings: ");
                for(int i = 0; i < toppings.Count; i++)
                {
                    System.Console.WriteLine(" "+ toppings[i]);
                }
            }
     
            public void Bake()
            {
                System.Console.WriteLine("Bakefor 25 minutes at 350");
            }
     
            public void Cut()
            {
                System.Console.WriteLine("Cuttingthe pizza into diagonal slices");
            }
     
            public void Box()
            {
                System.Console.WriteLine("Placepizza in official PizzaStore box");
            }
     
            public string GetName()
            {
                return name;
            }
    }
    2.2.3.2 具体的Pizza类
    public class NYStyleCheesePizza : Pizza
        {
            public NYStyleCheesePizza()
            {
                name = "NY StyleSauc and Cheese Pizza";  
                dough="Thin Crust Dough";      
                sauce="Marinara Sauce";
                toppings.Add("GratedReggiano Cheese");
            }
        }

    2.2  总结

    所有工厂模式都用来封装对象创建,工厂方法模式通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。

    工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。

    创建者(Creator)类

    产品类

    简单工厂和工厂方法之间的差异?

    简单工厂是在一个地方把所有的事都处理完了,然而工厂方法却是创建一个框架,让子类决定要如何实现。简单工厂的做法,可以将对象的创建封装起来,但是简单工厂不具备工厂方法的弹性,因为简单工厂不能变更正在创建的产品。

    3.   依赖倒置原则

    要依赖抽象,不要依赖具体类

    不能让高层组件依赖底层组件,而且不管高层、底层组件,两者都应该依赖于抽象。

    如何避免违反依赖倒置原则:

    l  变量不可以持有具体类的引用。

    如果使用new,则会持有具体类的引用,可以用工程来避开这样的做法

    l  不要让类派生自具体类。

    如果派生自具体类,你就会依赖具体类(请派生自一个抽象类或接口)

    l  不要覆盖基类中已实现的方法。

    如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享。

    4.   抽象工厂

    4.1 创建工厂接口

    回到上文的Pizza店,现在有新的需求,想要确保每家加盟店使用高质量的材料,打算创建一家生产原料的加工厂,并将原料送到各个加盟店。这个工厂负责创建原料家族中的每一种原料,工厂需要生产面团、酱料、芝士等。先为工厂定义一个接口,该接口负责所有原料:

     

    public interface PizzaIngredientFactory
        {
            Dough CreateDough();
     
           Sauce CreateSauce();
     
            Cheese CreateCheese();
     
            Veggies[] CreateVeggies();
     
            Pepperoni CreatePepperoni();
     
            Clams CreateClam();
    }

    3.2创建原料工厂

     

    public class NYPizzaIngredientFactory : PizzaIngredientFactory//具体原料工厂必须实现这个接口
        {
            public Dough CreateDough()
            {
                return new ThinCrustDough();
            }
     
            public Sauce CreateSauce()
            {
                return new MarinaraSauce();
            }
     
            public Cheese CreateCheese()
            {
                return new ReggianoCheese();
            }
     
            public Veggies[] CreateVeggies()
            {
                Veggies[] veggies = new Veggies[]{ new Garlic(), new Onion(), new Mushroom(), new RedPepper() };
                return veggies;
            }
     
            public Pepperoni CreatePepperoni()
            {
                return new SlicedPepperoni();
            }
     
            public Clams CreateClam()
            {
                return new FreshClams();
            }
    }

    4.3 重新抽象Pizza类

     

    public abstract  class Pizza
        {
           public string name;
           public Dough dough;
           public Sauce sauce;
           public Veggies[] veggies;
           public Cheese cheese;
           public Pepperoni pepperoni;
           public Clams clam;
           public ArrayList toppings = newArrayList();
     
           public abstract void Prepare();//把Prepare()方法声明成抽象,在这个方法中,我们需要收集Pizza所需的原材料,而这些原材料来自原料工厂。
            public void Bake()
            {
                System.Console.WriteLine("Bakefor 25 minutes at 350");
            }
     
            public void Cut()
            {
                System.Console.WriteLine("Cuttingthe pizza into diagonal slices");
            }
     
            public void Box()
            {
                System.Console.WriteLine("Placepizza in official PizzaStore box");
            }
     
            public string GetName()
            {
                return name;
            }
        }

    4.4 重新实现Pizza

      

    public class NYStyleCheesePizza : Pizza
        {
            PizzaIngredientFactory ingredientFactory;
            public NYStyleCheesePizza(PizzaIngredientFactoryingredientFactory)//制作Pizza需要工厂提供原材料,
    所以每个pizza类都需要从构造器中得到一个工厂,并将工厂存储在变量中
            {
                this.ingredientFactory =ingredientFactory;
                name = "NY StyleSauc and Cheese Pizza";         
                toppings.Add("GratedReggiano Cheese");
            }
     
            public override void Prepare()
            {
                System.Console.WriteLine("Preparing" + name);
                dough = ingredientFactory.CreateDough();
                sauce = ingredientFactory.CreateSauce();
                cheese = ingredientFactory.CreateCheese();
            }
       
        }

    4.5 重新生产pizza

     

    public class MYPizzaStore:PizzaStore
        {
            public  override Pizza CreatePizza(string type)
            {
                Pizza pizza=null;
                PizzaIngredientFactory ingredientFactory= new NYPizzaIngredientFactory();
                switch(type)
                {
                    case "cheese":
                        pizza = new NYStyleCheesePizza(ingredientFactory);
                        break;
                    case "veggie":
                        pizza=new NYStyleVeggiePizza(ingredientFactory);
                        break;
                    case "clam":
                        pizza=new NYStyleClamPizza(ingredientFactory);
                        break;
                    case "pepperoni":
                        pizza=new NYStylePepperoniPizza(ingredientFactory);
                        break;
                }
                return pizza;
            }
        }

    通过这一系列的操作,我们引入了新类型的工厂,也就是所谓的“抽象工厂”,来创建pizza原来家族。通过抽象工厂所提供的接口创建产品家族,利用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同上下文中实现各式各样的工厂,制造出各种不同的产品。

    4.6 定义抽象工厂模式

    抽象工厂模式提供一个接口,用于创建相关或依赖对象家族,而且不需要致命具体类。

    抽象工厂模式允许客户使用抽象的接口来创建一组相关的产品,而不需要知道实际产出的具体产品是什么,客户就从具体的产品中被解耦。

    4.7 抽象工厂与工厂方法的对比

    抽象工厂和工厂方法都是负责创建对象。

    抽象工厂是通过对象的组合

    定义了一个创建对象的接口,但由于子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。

    所以利用工厂方法创建对象时,需要扩展一个类,并覆盖它的工厂方法。

    整个工厂方法模式,只不过就是通过子类来创建对象,这种做法,客户只需要知道他们所使用的抽象类型就可以了,而由子类负责决定具体类型。将客户从具体类型中解耦。

    工厂方法是继承。

    抽象工厂方法是将一群相关的产品集合起来,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

  • 相关阅读:
    【转】理解Ruby的4种闭包:blocks, Procs, lambdas 和 Methods
    折腾weibo开放平台
    netsh——常用命令及使用技巧
    The Enemies of Achievement
    java内存模型
    命名规则
    JS 控制加载页面对象
    点击图片弹出上传文件对话框
    ASP.NET判断用户是否在线
    ASP.Net处理QueryString函数汉字参数传递错误
  • 原文地址:https://www.cnblogs.com/Olive116/p/6126991.html
Copyright © 2011-2022 走看看