总结 - 写在前面
适用的场景?
- 系统不依赖于产品实例的具体创建、组合及表达方式,这是应用所有工厂模式的基础。
- 系统需要由多个系列的产品组合以形成得到一个产品簇(即多个产品系列的具体产品需要组合),并且需要支持动态切换产品簇
- 需要提供一个具有统一接口的能直接获取一整产品簇的类库,使得客户端无需关心具体产品的创建组合等细节。
本质?
选择产品簇的实现。
解决了什么问题?
工厂模式的遗留问题: 一个具体工厂只能创建一类产品
体现了设计模式中的什么原则?
更符合开闭原则- 新增一种抽象产品类的时候,只需要添加相应的具体产品类和相应的工厂子类即可单一职责- 每个具体工厂类只负责创建具体抽象产品(或组合)对应的产品实现类- 非常容易增加具体工厂,仅需要分别实现抽象工厂的抽象部分,如有需要扩展抽象产品也可以扩展实现抽象产品簇的抽象部分;扩充现有的具体产品则需要实现相应抽象产品的抽象部分
和它相关的设计模式?
简单工厂 + 工厂模式
工厂方法模式或简单工厂关注的是单个产品对象的创建;而抽象工厂模式需要注重产品簇对象的创建。
而抽象工厂解决: 需创建一系列产品对象,它们是构建新对象的组成部分,相互间有约束。
三个工厂模式之间的联系
抽象工厂创建的产品簇若简化为只有一个产品,此时抽象工厂与工厂方法差不多,可退化,工厂方法又可退化成简单工厂,此为它们的联系。
抽象工厂的实现中,还可以使用工厂方法来提供抽象工厂的具体实现,彼此之间可以组合使用。
1. 快速入门
目标
提供一个接口,用于创建 相关地对象家族;不关心零件的具体实现,而是只关心接口 (API)。仅使用该接口 (API) 将零件组装成为产品
类图
- 抽象工厂模式关注于创建多个相关的对象。
- 工厂方法模式则专注于创建单一对象。
- 在抽象工厂模式里,客户端利用组合的方式调用多个方法以创建一组对象。
- 而工厂方法模式倾向于采用继承机制来完成对象的构建。
模式组成
| 组成(角色) | 关系 | 作用 |
|---|---|---|
抽象产品族 AbstractProduct | 抽象产品的父类 | 描述抽象产品的公共接口 |
抽象产品 Product | 具体产品的父类 | 描述具体产品的公共接口 |
具体产品 Concrete Product | 抽象产品的子类;工厂类创建的目标类 | 描述生产的具体产品 |
抽象工厂 AbstractFactory | 具体工厂的父类 | 描述具体工厂的公共接口 |
具体工厂 ConcreteFactory | 抽象工厂的子类;被外界调用 | 描述具体工厂;实现 FactoryMethod 工厂方法创建产品的实例 |
实现步骤
- 创建抽象工厂类,定义具体工厂的公共接口
- 创建抽象产品族类,定义抽象产品的公共接口
- 创建抽象产品类(继承抽象产品族类),定义具体产品的公共接口
- 创建具体产品类(继承抽象产品类),定义生产的具体产品
- 创建具体工厂类 (继承抽象工厂类),定义创建对应具体产品实例的方法
- 客户端通过实例化具体的工厂类,调用其创建不同目标产品的方法创建不同具体产品
2. 入门案例
小成有两间塑料加工厂,A 厂产容器类,B 厂产模具类。现 A 厂地客户需模具类,B 厂地客户需容器类。冲突是无资源开新分厂。解决方案为在原有两厂增设所需功能,A 厂产容器和模具,B 厂也如此。
UML 设计
代码实现
- 创建抽象工厂类,定义具体工厂的公共接口
public abstract class Factory {
public abstract AbstractProduct manufactureContainer();
public abstract AbstractProduct manufactureMould();
}
- 创建抽象产品族类,定义具体产品的公共接口
public abstract class AbstractProduct {
public abstract void show();
}
- 创建抽象产品类(继承抽象产品族类),定义具体产品的公共接口
public abstract class ContainerProduct extends AbstractProduct {
@Override
public abstract void show();
}
public abstract class MouldProduct extends AbstractProduct {
@Override
public abstract void show();
}
- 创建具体产品类(继承抽象产品类),定义生产的具体产品
public class ContainerProductA extends ContainerProduct {
@Override
public void show() {
System.out.println("生产除了容器产品 A");
}
}
public class ContainerProductB extends ContainerProduct {
@Override
public void show() {
System.out.println("生产出了容器产品 B");
}
}
public class MouldProductA extends MouldProduct {
@Override
public void show() {
System.out.println("生产出了磨具产品 A");
}
}
public class MouldProductB extends MouldProduct {
@Override
public void show() {
System.out.println("生产出了磨具产品 B");
}
}
- 创建具体工厂类 (继承抽象工厂类),定义创建对应具体产品实例的方法
//生产抽象产品的 A 类具体产品
public class FactoryA extends Factory {
@Override
public AbstractProduct manufactureContainer() {
return new ContainerProductA();
}
@Override
public AbstractProduct manufactureMould() {
return new MouldProductA();
}
}
// 生产抽象产品的 B 类具体产品
public class FactoryB extends Factory {
@Override
public AbstractProduct manufactureContainer() {
return new ContainerProductB();
}
@Override
public AbstractProduct manufactureMould() {
return new MouldProductB();
}
}
- 客户端通过实例化具体的工厂类,调用其创建不同目标产品的方法创建不同具体产品
public class Main {
public static void main(String[] args) {
Factory factoryA = new FactoryA();
Factory factoryB = new FactoryB();
//A厂生产容器 A
factoryA.manufactureContainer().show();
//A厂需要磨具 A
factoryA.manufactureMould().show();
//B 厂生产容器B
factoryB.manufactureContainer().show();
//B 厂生产磨具 B
factoryB.manufactureMould().show();
/*
输出:
生产除了容器产品 A
生产出了磨具产品 A
生产出了容器产品 B
生产出了磨具产品 B
*/
}
}
3. 提升案例
题目来源:卡码网KamaCoder
题目描述
小明家新开了两个工厂用来生产家具,一个生产现代风格的沙发和椅子,一个生产古典风格的沙发和椅子,现在工厂收到了一笔订单,请你帮他设计一个系统,描述订单需要生产家具的信息。
输入输出
UML 设计
- 抽象产品簇具有公共方法
show()用于输出信息 - 抽象工厂类具有制造 Chair 和 Sofa 的能力
- 具体工厂类根据具体的要求生产
classic或者modern产品组
由于需要写入到一个文件内实现,因此各个 class 类暂不用
public修饰
- 创建抽象工厂类,定义具体工厂的公共接口
//抽象工厂
abstract class AbstractFactory {
//制造抽象产品 Chair
protected abstract Product manufactureChair();
//制造抽象产品 Sofa
protected abstract Product manufactureSofa();
}
- 创建抽象产品族类,定义抽象产品的公共接口
//抽象产品簇
abstract class Product {
//输出信息
protected abstract void show();
}
- 创建抽象产品类(继承抽象产品族类),定义具体产品的公共接口
abstract class Chair extends Product {
@Override
protected abstract void show();
}
abstract class Sofa extends Product {
@Override
protected abstract void show();
}
- 创建具体产品类(继承抽象产品类),定义生产的具体产品
class ClassicChair extends Chair {
@Override
public void show() {
System.out.println("classical chair");
}
}
class ModernChair extends Chair {
@Override
public void show() {
System.out.println("modern chair");
}
}
class ClassicSofa extends Sofa {
@Override
public void show() {
System.out.println("classical sofa");
}
}
class ModernSofa extends Sofa {
@Override
public void show() {
System.out.println("modern sofa");
}
}
- 创建具体工厂类 (继承抽象工厂类),定义创建对应具体产品实例的方法
public class ClassicFactory extends AbstractFactory {
@Override
public Product manufactureChair(){
return new ClassicChair();
}
@Override
public Product manufactureSofa(){
return new ClassicSofa();
}
}
public class ModernFactory extends AbstractFactory {
@Override
public Product manufactureChair(){
return new ModernChair();
}
@Override
public Product manufactureSofa(){
return new ModernSofa();
}
}
- 客户端通过实例化具体的工厂类,调用其创建不同目标产品的方法创建不同具体产品
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 执照 Classic
AbstractFactory classicFactory = new ClassicFactory();
// 制造 Modern 产品组的工厂
AbstractFactory modernFactory = new ModernFactory();
// 制造总次数
int total = Integer.valueOf(scanner.nextLine());
for(int i = 0; i < total; i++) {
String groupType = scanner.nextLine();
if("modern".equals(groupType)) {
modernFactory.manufactureChair().show();
modernFactory.manufactureSo fa().show();
} else if ("classical".equals(groupType)) {
classicFactory.manufactureChair().show();
classicFactory.manufactureSofa().show();
}
}
}
}
4. 优缺点
优点
- 降低耦合
具体产品类的创建延迟到具体工厂中,这样将对象的创建封装起来;可以减少客户端与具体产品类之间的依赖。从而使得系统耦合度低,这样更加利于后期的维护和扩展。
- 更加符合 ⌈开闭 - 原则⌋
新增一种抽象产品类的时候,可以继承抽象产品簇实现抽象部分;如果需要新增一种产品组合,比如上述例子中增加 ⌈简约⌋ 风格的产品组,只需要增加相应的具体产品类和相应的工厂子类。
- 符合 ⌈单一职责⌋ 原则
每个具体工厂类只负责创建对应的产品
- 方便切换产品簇
客户端可以选用不同的工厂实现,就相当于切换不同的产品簇
缺点
- 很难支持为当前产品簇添加一个新的产品
抽象工厂接口中一级确定了可以被创建的产品类型组合(比如:Chair + Sofa)。如果需要添加新的产品则必须要修改抽象工厂的接口,这样就涉及到抽象工厂类和其所有子类具体工厂的改变。
此外,还需要实现抽象产品类和添加具体产品类。
违背了 ⌈开闭原则⌋
对于新的产品簇符合开闭原则;但是对于新的产品种类不符合 ⌈开闭原则⌋
- 很容易造成类层次复杂化
5. 提升
进一步认识抽象工厂
定义
提供了一个创建 一系列相关或者 相互依赖 对象的接口,而无需指定它们具体的类
解决思路: 既要创建接口的对象,也需要约束它们之间的关系
工厂方法之间的区别: ⌈工厂模式⌋ 或者 ⌈简单工厂⌋ 关注的是 ⌈单个产品对象的创建⌋
要解决的问题是: 要创建一系列的产品对象,而且这一系列对象是构建新的对象所需要的组成部分,也就是这一系列被创建的对象相互之间要有约束
抽象工厂起约束作用,提供统一外观供客户端使用。
当需要切换一个产品簇的时候,只要提供不同的抽象工厂实现就好了,也就是说现在是以产品簇作为一个整体被切换。
思考
本质: 选择产品簇的实现
⌈工厂方法⌋ 是选择单个产品的实现,虽然一个类里面可以由多个工厂方法,但是这些方法之间一般是没有联系的,即使看起来像是有联系
但是 ⌈抽象工厂⌋ 着重的就是为一个产品簇选择实现,定义在抽象工厂里面的方法通常是有联系的,它们都是产品的某一部分或者是相互依赖的。如果抽象工厂里面只定义了一个方法,直接创建产品,那么就退化成为工厂方法了
何时选用抽象工厂模式?
- 一个系统不要求依赖产品类实例如何创建、组合和表达,而不关系实现的时候
- 一个系统要由多个产品系列中的一个来配置的时候;可以动态的切换产品簇的时候
- 系统要求提供一个产品类的库,所有产品以同样的接口出现,客户端类不需要依赖具体实现
比较抽象工厂 VS 工厂模式
- 工厂方法模式:一般是针对单独的产品对象创建
- 抽象工厂模式:每个工厂可以创建多种产品
抽象工厂创建的产品簇若简化为只有一个产品,此时抽象工厂与工厂方法类似,抽象工厂模式可退化成工厂方法,工厂方法又能退化成简单工厂,这是它们的联系。
在抽象工厂的实现中,还可以使用工厂方法来提供抽象工厂的具体实现,也就是说它们可以组合使用。
抽象方法和单例模式
可以组合使用
在抽象工厂模式里面,具体的工厂实现,在整个应用中,通常一个产品系列只需要一个实例就可以了。因此可以把具体的工厂实现成为单例。
再谈优缺点
优点:
- 分离接口和实现
- 客户端用抽象工厂创建对象,不知具体实现,面向产品接口编程,从具体产品实现中解耦。
- 使得切换产品簇容易
- 因为一个具体的工厂实现代表的一个产品簇;客户端选用不同的工厂实现,就相当于是在切换不同的产品簇
- 符合开闭原则 + 单一职责原则 + 基于继承的等级结构
- 相比于简单工厂模式需要修改工厂类的判断逻辑,新增一种 **产品类 ** 的时候,只需要增加相应的 具体产品类 和 相应的工厂子类 即可
- 每个具体工厂类只负责创建对应的产品;而简单工厂需要判断生产的产品类
- 不使用静态工厂方法,可以行程基于继承的等级结构;而简单工厂模式的工厂类使用静态工厂方法
缺点:
- 不太容易扩展新的产品
添加新产品组合/集合需 修改抽象工厂逻辑或者修改具体工厂逻辑,会影响所有工厂实现类。虽有可扩展工厂的方式,但不够安全。
- 容易造成类层次复杂
抽象工厂实现需分层,每层负责一种选择,屏蔽一种变化,但易造成复杂类层次结构。
参考
- 《图解设计模式》 - Factory / Abstract Factory
- 抽象工厂-Carson
- 抽象工厂方法详解