zoukankan      html  css  js  c++  java
  • 浅析设计模式(三)——抽象工厂模式

    抽象工厂模式(Abstract-Factory,创建型模式)

    本文的结构:

    一、抽象工厂模式的定义

      提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

      前面【浅析设计模式(四)——创建型模式之Simple-Factory(简单工厂方法,非设计模式)】中介绍的简单工厂方法,虽然已经对变化的部分进行了封装,但是这里只由一个对象负责所有的具体类的实例化,因此每次有新增对象类型时,都需要改变工厂的源码进行扩展。而【浅析设计模式(五)——创建型模式之Factory-Method(工厂方法模式)】中介绍的工厂方法模式,则对简单工厂方法进行了最直接的封装和抽象,把创建对象的具体操作进行了分离,便于扩展和维护。这两种做法都是用于创建单类对象。

      从某种角度来看,抽象工厂模式也是类似的,都是把创建对象的动作进行了封装。但是抽象工厂模式意在创建相关或依赖对象的家族。从抽象工厂模式的实现来看,它通过组合委托给具体的工厂来实现,对象的创建被实现在工厂接口所暴露出来的方法中,通常用于创建产品家族或者相关集合。而这些工厂通常使用工厂方法来实现具体的产品创建。做个最简化,如果抽象工厂也只是创建一类产品,而不考虑后续的扩展之类的,是可以直接用工厂方法来实现的。

      这里有必要对“相关或依赖对象的家族”做一个简单地说明,举个栗子,假如要创建的对象是台电脑,那么像键盘、鼠标、显示器、主机等等,就可以看成是“相关或依赖对象的家族”。下面要举的栗子也是类似的,也还是拿Pizza的例子,Pizza有各种原料,这里从简,只举出Cheese奶酪、Veggies蔬菜、Clams蛤等原料进行说明,这里的创建对象,实际上是创建了包含各类原料的Pizza,也即就是提到的“相关或依赖对象的家族”。

    二、抽象工厂模式的参与者及其角色

    1. AbstractFactory

    • 声明一个创建抽象产品对象的操作接口

    2. ConcreteFactory

    • 实现创建具体产品对象的操作

    3. AbstractProduct

    • 为一类产品对象声明一个接口

    4. ConcreteProduct

    • 定义一个将被相应的具体工厂创建的产品对象
    • 实现AbstractProduct接口

    5. Client

    • 仅使用由AbstractFactory和AbstractProduct类声明的接口。

    三、抽象工厂模式的类图 

    四、抽象工厂模式的示例

    1. AbstractFactory:声明一个创建抽象产品对象的操作接口,用于创建相关或依赖对象的家族。

     1 /**
     2  * 
     3  * <p>
     4  * Reference : Head-First-Design-Patterns
     5  *
     6  */
     7 public interface PizzaIngredientFactory {
     8     public Cheese createCheese();
     9     public Veggies[] createVeggies();
    10     public Clams createClam();
    11 }

    2. ConcreteFactory:实现创建具体产品对象的操作,这里就是最简单的直接创建对象,

    例1:

     1 /**
     2  * 
     3  * <p>
     4  * Reference : Head-First-Design-Patterns
     5  *
     6  */
     7 public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
     8     
     9     @Override
    10     public Veggies[] createVeggies() {
    11         Veggies veggies[] = { new Garlic(), new Onion(), new Mushroom()};
    12         return veggies;
    13     }
    14 
    15     @Override
    16     public Clams createClam() {
    17         return new FreshClams();
    18     }
    19 
    20     @Override
    21     public Cheese createCheese() {
    22         return new ReggianoCheese();
    23     }
    24 }

     例2

     1 /**
     2  * 
     3  * <p>
     4  * Reference : Head-First-Design-Patterns
     5  *
     6  */
     7 public class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory{
     8     
     9     @Override
    10     public Cheese createCheese() {
    11         return new MozzarellaCheese();
    12     }
    13     @Override
    14     public Veggies[] createVeggies() {
    15         Veggies veggies[] = { new Onion(), new Garlic()};
    16         return veggies;
    17     }
    18     @Override
    19     public Clams createClam() {
    20         return new FrozenClams();
    21     }
    22 }

     从这里可以看出,上面的具体工厂其实就是工厂方法的简化,不带任何判断就直接创建了具体的对象。当然也是可以像前面提到的工厂方法模式一样,带参数,然后根据参数进行具体对象的创建。

    3. AbstractProduct:为一类产品对象声明一个接口,以上创建的“相关或依赖对象的家族”其实就是这里需要用到的。

     1 /**
     2  * 
     3  * <p>
     4  * Reference : Head-First-Design-Patterns
     5  *
     6  */
     7 public abstract class Pizza {
     8     String name;
     9 
    10     Veggies veggies[];
    11     Cheese cheese;
    12     Clams clam;
    13 
    14     abstract void prepare();
    15 
    16     void bake() {
    17         System.out.println("Bake for 25 minutes at 350");
    18     }
    19 
    20     void cut() {
    21         System.out.println("Cutting the pizza into diagonal slices");
    22     }
    23 
    24     void box() {
    25         System.out.println("Place pizza in official PizzaStore box");
    26     }
    27 
    28     void setName(String name) {
    29         this.name = name;
    30     }
    31 
    32     String getName() {
    33         return name;
    34     }
    35 
    36     public String toString() {
    37         StringBuffer result = new StringBuffer();
    38         result.append("---- " + name + " ----
    ");
    39         
    40         if (cheese != null) {
    41             result.append(cheese);
    42             result.append("
    ");
    43         }
    44         if (veggies != null) {
    45             for (int i = 0; i < veggies.length; i++) {
    46                 result.append(veggies[i]);
    47                 if (i < veggies.length-1) {
    48                     result.append(", ");
    49                 }
    50             }
    51             result.append("
    ");
    52         }
    53         if (clam != null) {
    54             result.append(clam);
    55             result.append("
    ");
    56         }
    57         return result.toString();
    58     }
    59 }

     4. ConcreteProduct:实现AbstractProduct接口,定义一个将被相应的具体工厂创建的产品对象。

    这里简单地举个栗子即可,可以看到,这里组合使用了创建对象的工厂,然后根据需要由工厂创建具体的对象。

     1 /**
     2  * 
     3  * <p>
     4  * Reference : Head-First-Design-Patterns
     5  *
     6  */
     7 public class VeggiePizza extends Pizza{
     8     PizzaIngredientFactory ingredientFactory;
     9     
    10     public VeggiePizza(PizzaIngredientFactory ingredientFactory) {
    11         this.ingredientFactory = ingredientFactory;
    12     }
    13  
    14     @Override
    15     void prepare() {
    16         System.out.println("Preparing " + name);
    17         cheese = ingredientFactory.createCheese();
    18         veggies = ingredientFactory.createVeggies();
    19     }
    20 }

     类似的

     1 /**
     2  * 
     3  * <p>
     4  * Reference : Head-First-Design-Patterns
     5  *
     6  */
     7 public class ClamPizza extends Pizza{
     8     PizzaIngredientFactory ingredientFactory;
     9      
    10     public ClamPizza(PizzaIngredientFactory ingredientFactory) {
    11         this.ingredientFactory = ingredientFactory;
    12     }
    13  
    14     void prepare() {
    15         System.out.println("Preparing " + name);
    16         cheese = ingredientFactory.createCheese();
    17         clam = ingredientFactory.createClam();
    18     }
    19 }

     5. Client:仅使用由AbstractFactory和AbstractProduct类声明的接口。

      这里的例子曲折了点,不过也可以大概说明要表达的意思。获取到具体Pizza的流程如下:

    1. 首先创建一个纽约披萨店,即PizzaStore nyStore = new NYPizzaStore();
    2. 通过披萨店下订单,即Pizza pizza = nyStore.orderPizza("cheese");
    3. orderPizza("cheese")首先调用Pizza pizza = createPizza("cheese");
    4. createPizza("cheese")中就涉及到了具体的工厂类,pizza = new CheesePizza(ingredientFactory);
    5. 接下来就是具体的准备Pizza了,调用pizza.prepare()时,便是由工厂具体地创建所有对象家族了。如下
    1 void prepare() {
    2     System.out.println("Preparing " + name);
    3     cheese = ingredientFactory.createCheese();
    4     //...其他的原料产品,如Veggies、Clams等,可能只需要一个,也可能全部都要。这里略去
    5 }

     顺着这个思路走:

    PizzaStore nyStore = new NYPizzaStore()  →

        Pizza pizza = nyStore.orderPizza("cheese")  →

            Pizza pizza = createPizza("cheese")  →

                pizza = new CheesePizza(ingredientFactory)  →

                    pizza.prepare()  →

                        ingredientFactory创建具体的产品。。。

    测试用的主类和主函数:

     1 /**
     2  * This is the test-main.
     3  * <p>
     4  * Reference : Head-First-Design-Patterns
     5  *
     6  */
     7 public class PizzaTestApp {
     8     public static void main(String[] args) {
     9         PizzaStore nyStore = new NYPizzaStore();
    10         PizzaStore chicagoStore = new ChicagoPizzaStore();
    11  
    12         Pizza pizza = nyStore.orderPizza("cheese");
    13         System.out.println("Ethan ordered a " + pizza + "
    ");
    14  
    15         pizza = chicagoStore.orderPizza("cheese");
    16         System.out.println("Joel ordered a " + pizza + "
    ");
    17 
    18         pizza = nyStore.orderPizza("clam");
    19         System.out.println("Ethan ordered a " + pizza + "
    ");
    20  
    21         pizza = chicagoStore.orderPizza("clam");
    22         System.out.println("Joel ordered a " + pizza + "
    ");
    23 
    24 
    25         pizza = nyStore.orderPizza("veggie");
    26         System.out.println("Ethan ordered a " + pizza + "
    ");
    27  
    28         pizza = chicagoStore.orderPizza("veggie");
    29         System.out.println("Joel ordered a " + pizza + "
    ");
    30     }
    31 }

     抽象类PizzaStore

     1 /**
     2  * 
     3  * <p>
     4  * Reference : Head-First-Design-Patterns
     5  *
     6  */
     7 public abstract class PizzaStore {
     8     protected abstract Pizza createPizza(String item);
     9      
    10     public Pizza orderPizza(String type) {
    11         Pizza pizza = createPizza(type);
    12         System.out.println("--- Making a " + pizza.getName() + " ---");
    13         pizza.prepare();
    14         pizza.bake();
    15         pizza.cut();
    16         pizza.box();
    17         return pizza;
    18     }
    19 }

     继承抽象类PizzaStore的具体实现类NYPizzaStore。

     1 /**
     2  * 
     3  * <p>
     4  * Reference : Head-First-Design-Patterns
     5  *
     6  */
     7 public class NYPizzaStore extends PizzaStore{
     8     protected Pizza createPizza(String item) {
     9         Pizza pizza = null;
    10         PizzaIngredientFactory ingredientFactory = 
    11             new NYPizzaIngredientFactory();
    12  
    13         if (item.equals("cheese")) {
    14   
    15             pizza = new CheesePizza(ingredientFactory);
    16             pizza.setName("New York Style Cheese Pizza");
    17   
    18         } else if (item.equals("veggie")) {
    19  
    20             pizza = new VeggiePizza(ingredientFactory);
    21             pizza.setName("New York Style Veggie Pizza");
    22  
    23         } else if (item.equals("clam")) {
    24  
    25             pizza = new ClamPizza(ingredientFactory);
    26             pizza.setName("New York Style Clam Pizza");
    27  
    28         }
    29         return pizza;
    30     }
    31 }

    简单地总结,就是使用组合工厂类创建了包含一系列相关对象的具体产品对象。

     五、参考

    1、参考《Head First设计模式》和GoF《设计模式:可复用面向对象软件的基础》

    2、代码可参考【github传送门】、UML类图参考【github传送门

  • 相关阅读:
    Jquery开发技巧汇总
    (转)
    C#中的委托和时间
    asp.net中like 使用参数化(转)
    省市选择
    安卓对话框
    z转自 西西吹雪
    winform控件验证技术(转)
    C#获取网卡Mac地址 .(转)
    关于GDI+错误的修正
  • 原文地址:https://www.cnblogs.com/wpbxin/p/9237036.html
Copyright © 2011-2022 走看看