Bootstrap

【设计模式】第六篇 - 工厂方法模式

1,前言

如果我们说工厂方法模式是简单工厂的升级,那么这篇要说的抽象工厂模式就是工厂方法模式的升级

工厂方法模式:使用继承实现,将对象的创建委托给子类(将对象的实例化延迟到子类),
通过子类实现工厂方法创建对象,将产品的"实现"从使用中"解耦",降低了代码耦合度

这篇我们说一下抽象工厂模式,代码上继续延用工厂方法模式外卖快餐店各种饭的例子

2,需求场景和技术实现

抽象工厂Demo需求场景:

    连锁品牌餐厅X可以做4种饭:
        沙拉烤肉饭-配方:烤肉 沙拉酱汁 米饭 蔬菜
        沙拉鸡肉饭-配方:鸡肉 沙拉酱汁 米饭 蔬菜
        番茄烤肉饭-配方:烤肉 番茄酱汁 米饭 蔬菜
        番茄鸡肉饭-配方:鸡肉 番茄酱汁 米饭 蔬菜

    由于供货渠道和地域差异化,加盟店铺A和店铺B的制作食材有所不同:
        加盟店铺A:烤肉A 鸡肉A 沙拉酱汁A 番茄酱汁A 长粒米饭 生菜
        加盟店铺B:烤肉B 鸡肉B 沙拉酱汁B 番茄酱汁B 圆粒米饭 白菜

    每种饭食统一的制作步骤:
        准备食材->加工烹饪->装盒打包

分析需求:
    店铺A和B唯一的不同点就是制作每种"饭"的原料不同,其他制作流程无差异
    所以将变化的部分抽象出来:A的原料类,和B的原料类(原料抽象工厂A和B)

技术实现:
    创建抽象"饭"类,具有准备,烹饪,装盒打包方法
    每种"饭"类继承抽象"饭"类,重写各个步骤的实现(根据配方不同,需要的食材原料不同)
    每种饭的制作工艺虽然相同,但是A店铺和B店铺使用的原材料不同,所以四种"饭"饭别依赖不同的原材料类

    创建一个抽象原材料接口,每个方法用于创建原材料(依赖抽象原材料)
    实现抽象原材料接口(抽象工厂接口),创建原材料A类和原材料B类(具体工厂) 用于指定每种原材料所使用的具体原材料
    根据原材料的类别方别制作产品抽象类(供抽象工厂依赖)和具体产品类(供具体工厂依赖)

    创建店铺抽象类,产生一个饭对象,并控制制作流程(准备食材->加工烹饪->装盒摆放->打包外送)
    继承自店铺抽象类,创建具体店铺A和店铺B,重写创建具体饭的过程,并分别依赖原材料工厂A和工厂B,
    实现不同店铺,相同制作流程,相同饭使用不同原材料目的

上边说的代码实现流程看上去可能比较混乱,下面对比一下代码

由于工程类较多,为了逻辑清晰,贴一张代码结构图

结构说明:
    com.brave.food.abstractfactory.ingredient.meat
    com.brave.food.abstractfactory.ingredient.rice
    com.brave.food.abstractfactory.ingredient.sauce
    com.brave.food.abstractfactory.ingredient.veggies
    以上四个包中为全部6种原材料

    com.brave.food.abstractfactory.factory
    RiceIngredientFactory类为抽象原材料工厂接口,规定了每种原材料的创建
    RiceIngredientFactoryA和RiceIngredientFactoryB为实现抽象原材料工厂接口的具体原材料工厂A和B,
    控制每种原材料实例化的具体原材料是哪一种

    com.brave.food.abstractfactory.rice
    包中Food类为饭食超类,包含饭的属性(名称+6种原材料),和方法(准备,烹饪,打包)
    其余四个类继承自Food类,依赖具体原材料工厂创建不同的食材,重写准备阶段的材料组合

    com.brave.food.abstractfactory.store
    RiceStore为店铺抽象类,规定了饭的同一制作流程,并将创建具体饭食延迟到子类进行
    不同店铺依赖不同的原材料工厂,实现相同的饭使用不同的原材料

    com.brave.food.abstractfactory.Client 测试类

3,抽象工厂模式

抽象工厂:提供一个创建一系列相关或相互依赖对象家族的接口,而无需指定具体类
抽象工厂模式为产品系提供了解决方案

1)原材料的简单说明(类较多且意义不大,统一说明)

    // 原材料分为4类6种 存放于com.brave.food.abstractfactory.ingredient.*下
    protected Sauce salad;          // 沙拉酱汁
    protected Sauce tomato;         // 番茄酱汁
    protected Meat barbecue;        // 烤肉
    protected Meat chicken;         // 鸡肉
    protected Rice rice;            // 米饭
    protected Veggies veggies;      // 蔬菜

    肉类Meat
        烤肉A BarbecueA  烤肉B BarbecueB
        鸡肉A ChickenA   鸡肉B ChickenB
    酱汁Sauce
        沙拉酱汁A SaladSauceA 沙拉酱汁B SaladSauceB
        番茄酱汁A TomatoSauceA 番茄酱汁B TomatoSauceB
    米饭Rice
        长粒米饭 LongGrainRice 圆粒米饭 RoundedGrainRice
    蔬菜Veggies
        白菜 Cabbage 生菜 Lettuce

2)原材料抽象工厂

/**
 * 原料工厂接口
 * 
 *      生产各种原材料
 * 
 * @author Brave
 *
 */
public interface RiceIngredientFactory {

    public Meat createChicken();
    public Meat createBarbecue();
    public Sauce createTomato();
    public Sauce createSalad();
    public Rice createRice();
    public Veggies createVeggies();

}

3)具体原材料工厂A

实现抽象原材料工厂接口的具体原材料工厂A和B,控制每种原材料实例化的具体原材料是哪一种

package com.brave.food.abstractfactory.factory;

import com.brave.food.abstractfactory.ingredient.meat.BarbecueA;
import com.brave.food.abstractfactory.ingredient.meat.ChickenA;
import com.brave.food.abstractfactory.ingredient.meat.Meat;
import com.brave.food.abstractfactory.ingredient.rice.LongGrainRice;
import com.brave.food.abstractfactory.ingredient.rice.Rice;
import com.brave.food.abstractfactory.ingredient.sauce.SaladSauceA;
import com.brave.food.abstractfactory.ingredient.sauce.Sauce;
import com.brave.food.abstractfactory.ingredient.sauce.TomatoSauceA;
import com.brave.food.abstractfactory.ingredient.veggies.Lettuce;
import com.brave.food.abstractfactory.ingredient.veggies.Veggies;

/**
 * A原料工厂
 * 
 *  烤肉A
 *  鸡肉A
 *  番茄酱汁A
 *  长粒米
 *  生菜
 *
 * @author Brave
 *
 */
public class RiceIngredientFactoryA implements RiceIngredientFactory {

    @Override
    public Meat createChicken() {
        Meat chicken = new ChickenA();
        return chicken;
    }

    @Override
    public Meat createBarbecue() {
        Meat barbecue = new BarbecueA();
        return barbecue;
    }

    @Override
    public Sauce createTomato() {
        Sauce sauce = new TomatoSauceA();
        return sauce;
    }

    @Override
    public Sauce createSalad() {
        Sauce sauce = new SaladSauceA();
        return sauce;
    }

    @Override
    public Rice createRice() {
        Rice rice = new LongGrainRice();
        return rice;
    }

    @Override
    public Veggies createVeggies() {
        Veggies veggies = new Lettuce();
        return veggies;
    }

}

4)创建饭类统一抽象类

package com.brave.food.abstractfactory.rice;

import com.brave.food.abstractfactory.ingredient.meat.Meat;
import com.brave.food.abstractfactory.ingredient.rice.Rice;
import com.brave.food.abstractfactory.ingredient.sauce.Sauce;
import com.brave.food.abstractfactory.ingredient.veggies.Veggies;

/**
 * Food统一接口
 *      各种Food实现此接口
 *      规定了Food的制作环节和属性
 * 
 * @author Brave
 *
 */
public abstract class Food {

    private String name;            // 名称
    protected Sauce salad;          // 沙拉酱汁
    protected Sauce tomato;         // 番茄酱汁
    protected Meat barbecue;        // 烤肉
    protected Meat chicken;         // 鸡肉
    protected Rice rice;            // 米饭
    protected Veggies veggies;      // 蔬菜

    // 准备
    public abstract void prepare();

    // 烹饪
    public void cook(){
        System.out.println("Food-cook");
    }

    // 打包
    public void pack(){
        System.out.println("Food-pack");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void print() {

        if(name !=null ){
            System.out.println("name = " + name);
        }

        if(salad !=null ){
            System.out.println("salad = " + salad.toString());
        }

        if(tomato !=null ){
            System.out.println("tomato = " + tomato.toString());
        }

        if(chicken !=null ){
            System.out.println("chicken = " + chicken.toString());
        }

        if(barbecue !=null ){
            System.out.println("barbecue = " + barbecue.toString());
        }

        if(rice !=null ){
            System.out.println("rice = " + rice.toString());
        }

        if(veggies !=null ){
            System.out.println("veggies = " + veggies.toString());
        }

    }

}

5)具体饭食(以其中一种为例说明)

每种"饭"类继承抽象"饭"类,重写各个步骤的实现(根据配方不同,需要的食材原料不同)
每种饭的制作工艺虽然相同,但是A店铺和B店铺使用的原材料不同,所以四种"饭"饭别依赖不同的原材料类

package com.brave.food.abstractfactory.rice;

import com.brave.food.abstractfactory.factory.RiceIngredientFactory;

/**
 * 沙拉烤肉饭
 * 
 *  配方:烤肉 沙拉酱汁 米饭 蔬菜
 *  工厂A:烤肉A 沙拉酱汁A 长粒米饭 生菜
 *  工厂B:烤肉B 沙拉酱汁B 圆粒米饭 白菜
 * 
 *  继承自饭食抽象类,重写了准备过程prepare()
 * 
 * @author Brave
 *
 */
public class SaladBarbecueRice extends Food {

    RiceIngredientFactory ingredientFactory;

    public SaladBarbecueRice(RiceIngredientFactory ingredientFactory) {
        this.ingredientFactory = ingredientFactory;
    }

    @Override
    public void prepare() {

        System.out.println("Preparing " + getName());

        barbecue = ingredientFactory.createBarbecue();
        salad = ingredientFactory.createSalad();
        rice = ingredientFactory.createRice();
        veggies = ingredientFactory.createVeggies();

        this.print();
    }

}

6)创建店铺抽象类(工厂方法模式)

RiceStore为店铺抽象类,规定了饭的同一制作流程,并将创建具体饭食延迟到子类进行

package com.brave.food.abstractfactory.store;

import com.brave.food.abstractfactory.rice.Food;

/**
 * 饭店抽象类
 * 
 *      控制了统一的制作流程
 *      将创建具体对象的操作延迟到子类
 * 
 * @author Brave
 *
 */
public abstract class RiceStore {

    public Food orderRice(String type){

        Food rice;

        rice = this.createRice(type);

        System.out.println("-----------"+type+"----------");

        rice.prepare();
        rice.cook();
        rice.pack();

        return rice;
    }

    protected abstract Food createRice(String type);

}

7)创建具体店铺

RiceStoreA和RiceStoreB为继承自RiceStore的具体店铺
重写了各种饭的制作方法,不同店铺依赖不同的原材料工厂,实现相同的饭使用不同的原材料

package com.brave.food.abstractfactory.store;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import com.brave.food.abstractfactory.factory.RiceIngredientFactory;
import com.brave.food.abstractfactory.factory.RiceIngredientFactoryA;
import com.brave.food.abstractfactory.rice.Food;
import com.brave.food.abstractfactory.rice.SaladBarbecueRice;
import com.brave.food.abstractfactory.rice.SaladChickenRice;
import com.brave.food.abstractfactory.rice.TomatoBarbecueRice;
import com.brave.food.abstractfactory.rice.TomatoChickenRice;

/**
 * 饭店A
 * 
 *  使用A原料工厂提供制作材料
 *  
 * @author Brave
 *
 */
public class RiceStoreA extends RiceStore {

    @Override
    protected Food createRice(String type) {

        Food rice = null;
        RiceIngredientFactory ingredientFactory = new RiceIngredientFactoryA();

        if(type.equals("TomatoBarbecue")){
            rice = new TomatoBarbecueRice(ingredientFactory);
            rice.setName("TomatoBarbecueRice");
        } else if(type.equals("TomatoChicken")){
            rice = new TomatoChickenRice(ingredientFactory);
            rice.setName("TomatoChickenRice");
        } else if(type.equals("SaladChicken")){
            rice = new SaladChickenRice(ingredientFactory);
            rice.setName("SaladChickenRice");
        } else if(type.equals("SaladBarbecue")){
            rice = new SaladBarbecueRice(ingredientFactory);
            rice.setName("SaladBarbecueRice");
        }

//      try {
//          Constructor c = null;
//          Class clazz = null;
//          try {
//              clazz = Class.forName(TomatoBarbecueRice.class.getName());
//          } catch (ClassNotFoundException e) {
//              System.out.println("类不存在");
//              e.printStackTrace();
//          }
//          c = clazz.getConstructor(RiceIngredientFactory.class);
//          try {
//              Food rice1 = (Food) c.newInstance(ingredientFactory);
//              rice1.setName("SaladBarbecueRice");
//          } catch (InstantiationException e) {
//              System.out.println("不支持抽象类或接口");
//              e.printStackTrace();
//          } catch (IllegalAccessException e) {
//              System.out.println("没有足够权限,即不能访问私有对象");
//              e.printStackTrace();
//          } catch (IllegalArgumentException e) {
//              e.printStackTrace();
//          } catch (InvocationTargetException e) {
//              e.printStackTrace();
//          }    
//      } catch (NoSuchMethodException e) {
//          e.printStackTrace();
//      } catch (SecurityException e) {
//          e.printStackTrace();
//      } finally {
//          
//      }

        return rice;
    }

}

8)测试类

package com.brave.food.abstractfactory;

import com.brave.food.abstractfactory.store.RiceStore;
import com.brave.food.abstractfactory.store.RiceStoreA;
import com.brave.food.abstractfactory.store.RiceStoreB;

public class Client {

    public static void main(String[] args) {

        // 店铺A和店铺B
        RiceStore riceStoreA = new RiceStoreA();
        RiceStore riceStoreB = new RiceStoreB();

        // 番茄烤肉饭
//      riceStoreA.orderRice("TomatoBarbecue");
//      riceStoreB.orderRice("TomatoBarbecue");

        // 番茄鸡肉饭
//      riceStoreA.orderRice("TomatoChicken");
//      riceStoreB.orderRice("TomatoChicken");

        // 沙拉烤肉饭
//      riceStoreA.orderRice("SaladChicken");
//      riceStoreB.orderRice("SaladChicken");

        // 沙拉鸡肉饭
        riceStoreA.orderRice("SaladBarbecue");
        riceStoreB.orderRice("SaladBarbecue");

    }
}

打印输出:

-----------SaladBarbecue----------
Preparing SaladBarbecueRice
name = SaladBarbecueRice
salad = 沙拉酱汁A
barbecue = 烤肉A
rice = 长粒米饭
veggies = 生菜
Food-cook
Food-pack
-----------SaladBarbecue----------
Preparing SaladBarbecueRice
name = SaladBarbecueRice
salad = 沙拉酱汁B
barbecue = 烤肉B
rice = 圆粒米饭
veggies = 白菜
Food-cook
Food-pack

从Log输出可以看到,店铺A和B 制作相同的饭,制作流程相同,使用的原材料不同

4,抽象工厂模式总结+优缺点

原料工厂接口(抽象工厂接口:为产品家族提供接口)

生产各种原材料
    抽象工厂:提供一个创建一系列相关或相互依赖对象家族的接口,而无需指定具体类
    抽象工厂模式为产品系提供了解决方案
    抽象工厂适用场景:
        1,一个系列要独立于他的产品的创建,组合和表示时
        2,一个系统要由多个产品系列中的一个来配置时
        3,当要强调一系列相关的产品对象的设计以便进行联合使用时
        4,当要提供一个产品类库,而只要显示他们的接口而不是实现时

 优点:
 客户端经常需要切换配置(交换产品系列)时,客户端通过抽象接口来操纵实例,具体类名不会出现在客户端
 抽象工厂允许客户使用抽象接口来创建一组相关的产品,而不需要关心实际产出的具体产品是什么,使客户从具体产品中解耦

 缺点:
 抽象工厂模式便于交换产品系列的同时,再改动声明过产品类的地方进行大量修改
解决方法:
添加if,Switch分支判断或者反射(反射的实现方式已包含在代码中的注释部分)

通过抽象工厂提供的接口创建产品家族,使代码从实际工厂解耦,在不同的上下文实现各种工厂,制造不同产品
由于代码从实际产品中解耦,我们可以替换不同工厂来取得不同的行为

5,三种工厂模式

1)三种模式的比较

简单工厂:
虽然不是真正的设计模式,但却是一个简单的方法
去除了客户端和具体产品的依赖,增强了代码移植性

工厂方法模式:
使用继承实现,将对象的创建委托给子类(将对象的实例化延迟到子类),子类实现工厂方法来创建对象
客户端只需要知道所使用的抽象类型,不用关心具体类型
工厂方法模式,只负责将客户端从具体类型中解耦

抽象工厂模式:
使用对象组合实现,将对象的创建实现在工厂接口所暴露出来的方法中
定义创建产品家族的抽象类型,由子类定义产品产生的方法,实例化后传入使用抽象类型的代码中
抽象工厂模式,将客户端从所使用的实际具体产品中解耦

2)三种模式的优缺点

简单工厂-优点:
去除了客户端和具体产品的依赖,增强了代码移植性
简单工厂-缺点:
违背了开放-封闭原则
添加新产品比较麻烦

工厂方法模式-优点:
使用继承实现,将对象的创建委托给子类(将对象的实例化延迟到子类),子类实现工厂方法来创建对象
客户端只需要知道所使用的抽象类型,不用关心具体类型
工厂方法模式,只负责将客户端从具体类型中解耦
后台模块契合了开放-封闭原则
工厂方法模式-缺点:
添加新产品需要创建大量新的类
客户端部分仍然违反开放-封闭原则,只是后台判断逻辑转移到了前台

抽象工厂模式-优点:
使用对象组合实现,将对象的创建实现在工厂接口所暴露出来的方法中
定义创建产品家族的抽象类型,由子类定义产品产生的方法,实例化后传入使用抽象类型的代码中
抽象工厂模式,将客户端从所使用的实际具体产品中解耦
抽象工厂模式-缺点:
当新的产品加入导致抽象工厂接口需添加新的产品时,所有实现此接口的类都需要拓展,工作量大

3)工厂模式共同点:

1,封装对象创建过程
2,减少客户端和具体类之间的依赖,促进松耦合
3,符合依赖倒置原则,不能让高层组件依赖低层组件,依赖抽象不依赖具体