写点什么

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

用户头像
Brave
关注
发布于: 2021 年 10 月 18 日

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 SaladBarbecueRicename = SaladBarbecueRicesalad = 沙拉酱汁Abarbecue = 烤肉Arice = 长粒米饭veggies = 生菜Food-cookFood-pack-----------SaladBarbecue----------Preparing SaladBarbecueRicename = SaladBarbecueRicesalad = 沙拉酱汁Bbarbecue = 烤肉Brice = 圆粒米饭veggies = 白菜Food-cookFood-pack
复制代码


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



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


原料工厂接口(抽象工厂接口:为产品家族提供接口)
生产各种原材料 抽象工厂:提供一个创建一系列相关或相互依赖对象家族的接口,而无需指定具体类 抽象工厂模式为产品系提供了解决方案 抽象工厂适用场景: 1,一个系列要独立于他的产品的创建,组合和表示时 2,一个系统要由多个产品系列中的一个来配置时 3,当要强调一系列相关的产品对象的设计以便进行联合使用时 4,当要提供一个产品类库,而只要显示他们的接口而不是实现时
优点: 客户端经常需要切换配置(交换产品系列)时,客户端通过抽象接口来操纵实例,具体类名不会出现在客户端 抽象工厂允许客户使用抽象接口来创建一组相关的产品,而不需要关心实际产出的具体产品是什么,使客户从具体产品中解耦
缺点: 抽象工厂模式便于交换产品系列的同时,再改动声明过产品类的地方进行大量修改解决方法:添加if,Switch分支判断或者反射(反射的实现方式已包含在代码中的注释部分)
通过抽象工厂提供的接口创建产品家族,使代码从实际工厂解耦,在不同的上下文实现各种工厂,制造不同产品由于代码从实际产品中解耦,我们可以替换不同工厂来取得不同的行为
复制代码



5,三种工厂模式


1)三种模式的比较

简单工厂:虽然不是真正的设计模式,但却是一个简单的方法去除了客户端和具体产品的依赖,增强了代码移植性
工厂方法模式:使用继承实现,将对象的创建委托给子类(将对象的实例化延迟到子类),子类实现工厂方法来创建对象客户端只需要知道所使用的抽象类型,不用关心具体类型工厂方法模式,只负责将客户端从具体类型中解耦
抽象工厂模式:使用对象组合实现,将对象的创建实现在工厂接口所暴露出来的方法中定义创建产品家族的抽象类型,由子类定义产品产生的方法,实例化后传入使用抽象类型的代码中抽象工厂模式,将客户端从所使用的实际具体产品中解耦
复制代码


2)三种模式的优缺点

简单工厂-优点:去除了客户端和具体产品的依赖,增强了代码移植性简单工厂-缺点:违背了开放-封闭原则添加新产品比较麻烦
工厂方法模式-优点:使用继承实现,将对象的创建委托给子类(将对象的实例化延迟到子类),子类实现工厂方法来创建对象客户端只需要知道所使用的抽象类型,不用关心具体类型工厂方法模式,只负责将客户端从具体类型中解耦后台模块契合了开放-封闭原则工厂方法模式-缺点:添加新产品需要创建大量新的类客户端部分仍然违反开放-封闭原则,只是后台判断逻辑转移到了前台
抽象工厂模式-优点:使用对象组合实现,将对象的创建实现在工厂接口所暴露出来的方法中定义创建产品家族的抽象类型,由子类定义产品产生的方法,实例化后传入使用抽象类型的代码中抽象工厂模式,将客户端从所使用的实际具体产品中解耦抽象工厂模式-缺点:当新的产品加入导致抽象工厂接口需添加新的产品时,所有实现此接口的类都需要拓展,工作量大
复制代码


3)工厂模式共同点:

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


用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【设计模式】第六篇 - 工厂方法模式