zoukankan      html  css  js  c++  java
  • 抽象工厂模式详解 —— head first 设计模式

    设计模式文章

    中介者模式

    代理模式

    抽象工厂模式详解 —— head first 设计模式

    装饰者模式

    适配器模式

    策略模式

    观察者模式

    建造者模式 (Builder)

    项目实例

    假设你有一家 pizza 店,你有很多种 pizza,要在系统中显示你所有 pizza 种类。实现这个功能并不难,使用普通方式实现:

    public class PizzaStore {
         Pizza orderPizza(String type) {
           Pizza pizza = null;
           if (type.equals("cheese")) {
             pizza = new CheesePizza();
           } else if (type.equals("clam")) {
               pizza = new ClamPizza();
           } else if (type.equals("veggie")) {
             pizza = new VeggiePizza();
           }
         pizza.prepare();
         pizza.bake();
         pizza.cut();
         pizza.box();
         return pizza;
       }
    }

    这种把选择披萨和制作过程全放在一起,如果新推出一款披萨就要修改 OrderPizza 方法,不符合开闭原则。

    我们可已经这段变化的代码移到一个专门的类中,这个类只管创建 Pizza,我们将这个类的对象称之为“工厂”;

    简单工厂(factory)

    根据“将变化抽离并封装“的原则,我们可以将创建 pizza 实例这一块给抽离出来,因为这块后边可能会新增一些别的 pizza 类型,由一个对象来专职创建 pizza 对象。如果有一个 SimplePizzaFactory,那么 orderPizza() 就变成此对象的客户;

    当客户下单,pizzaStore 调用 orderPizza() 的时候,就叫比萨工厂做一个,orderPizza() 只关心从工厂得到一个比萨;

    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 类:

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

    这样的话,就可以遵循了把变化的部分抽象成一个类。下次需要变化,只需要对变化的类的部分做修改即可。

    下面给出其UML图。

    image

    简单工厂的目的,主要负责实现生产对象的细节,根据订单来生产。每一个商店有对应着自己的工厂。在此,可以将 OrderPizza 是工厂的一个客户,当然还可以是其他类似的方法,比如 PizzaShopMenu 也可以是工厂的客户。客户就是为了获取工厂生成的对象。前者是为了卖给 people,后者是为了使用 Pizza 类获取其描述和价格等。这样遵循了找出变化的部分,将其封装到一个类中的原则,从而到达了复用的目的。

    简单工厂之所以简单是因为它只根据需要,来生产出来指定的 Pizza。

    特点:所有的产品都由一个工厂来生产。

    上面的简单工厂并不是一个真正的模式,只是一种编程习惯,这个不能算工厂模式,不过也算是基本满足需求。

    接下来我们来看看工厂方法的实现。

    工厂方法

    背景更新:

    假如现在你要开分店,各种加盟商进来后,他们都要开发符合本地口味的 pizza,那就需要各个地方都有一个工厂,也就是每个地方继承 SimpleFactory类,但是每个工厂并不是完全使用你原来的烘培方法。或许,我们可以像 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 Pizza CreatePizza(string pizzaType); //把工厂对象移到该方法中,该方法为抽象方法
       }

     对应于两家分店的代码:

    public class NYPizzaStore extends PizzaStore {
       @Override
       protected Pizza createPizza(String type) {
         if (type.equals("cheese")) {
           return new NYCheesePizza();
         }
         return null;
       }
    }
     
    public class ChicagoPizzaStore extends PizzaStore {
         @Override
         protected Pizza createPizza(String type) {
            if (type.equals("cheese")) {
           return new ChicagoCheesePizza();
         }
         return null;
       }
    }    

     我们需要建立一个 Pizza 实体类:

    public abstract class Pizza {
        String name; //名称
        String dough; //面团类型
        String sauce; //酱料
        ArrayList<String> toppings = new ArrayList<String>(); //作料
     
        void prepare() {
            System.out.println("准备 " + name);
            System.out.println("揉面团...");
            System.out.println("添加酱料...");
            System.out.println("添加作料: ");
            for (int i = 0; i < toppings.size(); i++) {
                System.out.println("   " + toppings.get(i));
            }
        }
        void bake() {
            System.out.println("烘烤25分钟");
        }
        void cut() {
            System.out.println("把Pizza对角切片");
        }
        void box() {
            System.out.println("把Pizza装盒子");
        }
        public String getName() {
            return name;
        }
    }

    然后需要一些具体的子类,下边定义两个子类:纽约风味的芝士披萨(NYStyleCheesePizza)、芝加哥风味的芝士披萨 (ChicageStyleCheesePizza)

    public class NYStyleCheesePizza extends Pizza {
        public NYStyleCheesePizza() { 
            name = "NY Style Sauce and Cheese Pizza";
            dough = "Thin Crust Dough";
            sauce = "Marinara Sauce";
            toppings.add("Grated Reggiano Cheese");
        }
    }
    
    public class ChicagoStyleCheesePizza extends Pizza {
        public ChicagoStyleCheesePizza() { 
            name = "Chicago Style Deep Dish Cheese Pizza";
            dough = "Extra Thick Crust Dough";
            sauce = "Plum Tomato Sauce";
            toppings.add("Shredded Mozzarella Cheese");
        }
        //可以覆盖cut()方法
        void cut() {
            System.out.println("Cutting the pizza into square slices");
        }
    }

    2.2  总结

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

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

    创建者(Creator)类

    产品类

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

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

    抽象工厂

    创建工厂接口

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

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

    创建一个原料厂

    //具体原料工厂必须实现这个接口
    public class NYPizzaIngredientFactory extends 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();
            }
    }

    重新抽象 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; } }

    重新实现 Pizza

    public class NYStyleCheesePizza extends 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(); } }

    重新生产pizza

    public class MYPizzaStore extends 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 原来家族。通过抽象工厂所提供的接口创建产品家族,利用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同上下文中实现各式各样的工厂,制造出各种不同的产品。

    定义抽象工厂模式

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

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

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

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

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

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

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

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

    工厂方法是继承。

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

     参考文章

    https://blog.csdn.net/xuemoyao/article/details/53437609

    https://www.cnblogs.com/lzhp/p/3375041.html

  • 相关阅读:
    linux下小知识点积累
    马斯洛需求层次理论
    tar命令的小经验
    shell 和c语言的区别
    使用vue实现的品牌列表简单小例子
    vue的基本代码以及常见指令
    MVC和MVVM
    CSS3幽灵
    Web版App,原生App,混合App的区别以及优缺点
    常见的sql操作
  • 原文地址:https://www.cnblogs.com/huansky/p/13629979.html
Copyright © 2011-2022 走看看