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 抽象工厂与工厂方法的对比

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

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

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

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

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

工厂方法是继承。

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

时间: 2024-10-25 22:19:45

Head First 设计模式之工厂模式(Factory Pattern)的相关文章

【设计模式】工厂模式 Factory Pattern

1)简单工厂(不是模式) 简单工厂只是一种变成习惯,并非23种设计模式之一. 简单工厂提供将实例话那种类型留给运行时判断,而非编译时指定.简单工厂模式就是由一个工厂类根据传入的参数决定创建出哪一个类的实例. 角色: 工厂类:接收参数,返回参数指定的类的实例. 抽象产品:返回实例的类型,具体产品的基类. 具体产品:继承自抽象产品的类. 2)工厂方法模式 沿着上面的思路,创建的接口是稳定的,但是创建对象剧烈变化.将稳定的接口抽象成基类,让子类确定实例哪个产品,将实际创建工作推迟到子类中. 工厂方法用

设计模式一 工厂模式Factory

设计模式一 工厂模式Factory 在面向对象编程中, 最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的.但是在一些情况下, new操作符直接生成对象会带来一些问题.举例来说, 许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象. 在这些情况,新对象的建立就是一个 "过程",不仅是一个操作,像一部大机器中的一个齿轮传动. 模式的问题:你如何能轻松方便

Android设计模式之一个例子让你彻底明白工厂模式(Factory Pattern)

提出疑问 这几天研究工厂模式的时候,看到网上的一些文章中举的例子我就很疑惑,我相信这也是许多人的疑惑:工厂模式的功能就是创建实例,我们创建实例直接new不就完了吗,干嘛还得再封装一层工厂类,然后用工厂类再去new出这个实例?这不多此一举吗? 比如我看到这样的例子,我们的用户分为金牌用户和银牌用户,我们要创建一个金牌用户或者银牌用户. 定义一个用户接口 public interface ICustomer { String describe(); } 金牌用户实现类 public class Go

23种设计模式--工厂模式-Factory Pattern

一.工厂模式的介绍       工厂模式让我们相到的就是工厂,那么生活中的工厂是生产产品的,在代码中的工厂是生产实例的,在直白一点就是生产实例的类,代码中我们常用new关键字,那么这个new出来的实例就就依赖与这个类,2者之间的耦合度就高,此时我们就可以使用面向对象的去解决这个问题,将变化点封装起来,这就是我们将要首先引入的简单工厂模式:先来说一个场景,比如我们吃水果这个例子吧,我们有时候想吃苹果,有时候想吃橘子,但是每次都需要去买这些水果,后来有个水果店,又卖橘子,又卖苹果,这个水果店就当于简

创建型模式篇(工厂模式Factory Pattern)

一.工厂模式(Factory Pattern) 1.定义: 在软件系统,经常面临着"某个对象"的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口.提供一种封装机制来隔离这个对象的变化,从而保持系统中其他依赖这个变化对象的对象,就要用到工厂模式. 2.目的:定义一个用户创建对象的接口,让子类决定实例化哪一个类,FactoryMethod使一个类的实例化延迟到它的子类. 3.结构图: 工厂模式:定义一个用于创建对象的接口,但是让子类决定实例化哪个

抽象工厂模式(abstarct factory pattern)和工厂模式(factory pattern)的比较

抽象工厂模式和工厂模式从字面上来看就有必然的联系,他们都是创建型模式.总结来说,工厂模式(factory pattern)只是个小工厂,只提供一层接口的实现类的输出,而抽象工厂模式(abstract factory pattern)是工厂模式的进一步升级,可以上升到两层以上的工厂模式继承,是工厂的工厂.一计算机来说,工厂模式可以是不同型号显示器,CPU或者网卡的提供者,而抽象工厂模式是显示器工厂.CPU工厂和网卡工厂的工厂.可以通过抽象工厂来获得CPU工厂,进而获得某个型号的CPU.整个依赖关系

工厂模式(factory pattern)

工厂模式主要用来封装对象的创建,有3种分类:简单工厂(simple factory).工厂方法(factory method).抽象工厂(abstract factory). 简单工厂包括3种组成元素:抽象产品.具体产品.具体工厂(简单工厂),结构图如下: C++实现: //抽象产品 class Car { public: virtual string getDescription() = 0; }; //具体产品 class Audi : public Car { string getDesc

设计模式~简单工厂模式(Factory)

简单工厂模式Simple Factory根据提供给它的数据,返回一个类的实例.通常它返回的类都有一个公共的父类(或者接口对象). 简单工厂的作用是实例化对象,而不需要客户了解这个对象属于哪个具体的子类.简单工厂实例化的类具有相同的接口或者基类,在子类比较固定并不需要扩展时,可以使用简单工厂.如数据库生产工厂就是简单工厂的一个应用.         采用简单工厂的优点是可以使用户根据参数获得对应的类实例,避免了直接实例化类,降低了耦合性:缺点是可实例化的类型在编译期间已经被确定,如果增加新类 型,

设计模式之工厂模式 Factory实现

simpleFactory //car接口 public interface Car { void run(); } //两个实现类 public class Audi implements Car{ public void run() { System.out.println("奥迪在跑"); } } public class Byd implements Car{ public void run() { System.out.println("Byd在跑");

设计模式之--工厂模式(Factory)

定义:Defined an interface for creating an object,but let subclasses decide which class  to instantiate.Factory Method let a class defer instantiation to subclass(定义一个创建对象的接口,让子类类型来决定实例化对象.工厂方法能够使类的实例化延迟到具体的子类)工厂方法的泛型应用是关键 类图: 一 定义产品 1 定义产品接口 package dm