【设计模式】第六篇 - 工厂方法模式
发布于: 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 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,符合依赖倒置原则,不能让高层组件依赖低层组件,依赖抽象不依赖具体
复制代码
划线
评论
复制
发布于: 2021 年 10 月 18 日阅读数: 20
Brave
关注
还未添加个人签名 2018.12.13 加入
还未添加个人简介
评论