品设计模式 - (创建型) 抽象工厂模式 Abstract Factory

223 阅读12分钟
这里写图片替代文字

总结 - 写在前面

适用的场景?

  • 系统不依赖于产品实例的具体创建、组合及表达方式,这是应用所有工厂模式的基础。
  • 系统需要由多个系列的产品组合以形成得到一个产品簇(即多个产品系列的具体产品需要组合),并且需要支持动态切换产品簇
  • 需要提供一个具有统一接口的能直接获取一整产品簇的类库,使得客户端无需关心具体产品的创建组合等细节。

本质?

选择产品簇的实现。

解决了什么问题?

工厂模式的遗留问题: 一个具体工厂只能创建一类产品

体现了设计模式中的什么原则?

  • 更符合开闭原则 - 新增一种抽象产品类的时候,只需要添加相应的具体产品类和相应的工厂子类即可
  • 单一职责- 每个具体工厂类只负责创建具体抽象产品(或组合)对应的产品实现类
  • 非常容易增加具体工厂,仅需要分别实现抽象工厂的抽象部分,如有需要扩展抽象产品也可以扩展实现抽象产品簇的抽象部分;扩充现有的具体产品则需要实现相应抽象产品的抽象部分

和它相关的设计模式?

简单工厂 + 工厂模式

工厂方法模式或简单工厂关注的是单个产品对象的创建;而抽象工厂模式需要注重产品簇对象的创建。

而抽象工厂解决: 需创建一系列产品对象,它们是构建新对象的组成部分,相互间有约束。

三个工厂模式之间的联系

抽象工厂创建的产品簇若简化为只有一个产品,此时抽象工厂与工厂方法差不多,可退化,工厂方法又可退化成简单工厂,此为它们的联系。

抽象工厂的实现中,还可以使用工厂方法来提供抽象工厂的具体实现,彼此之间可以组合使用。


1. 快速入门

目标

提供一个接口,用于创建 相关地对象家族;不关心零件的具体实现,而是只关心接口 (API)。仅使用该接口 (API) 将零件组装成为产品

类图

  • 抽象工厂模式关注于创建多个相关的对象。
  • 工厂方法模式则专注于创建单一对象。
  • 在抽象工厂模式里,客户端利用组合的方式调用多个方法以创建一组对象。
  • 而工厂方法模式倾向于采用继承机制来完成对象的构建。

画板

模式组成

组成(角色)关系作用
抽象产品族 AbstractProduct抽象产品的父类描述抽象产品的公共接口
抽象产品 Product具体产品的父类描述具体产品的公共接口
具体产品 Concrete Product抽象产品的子类;工厂类创建的目标类描述生产的具体产品
抽象工厂 AbstractFactory具体工厂的父类描述具体工厂的公共接口
具体工厂 ConcreteFactory抽象工厂的子类;被外界调用描述具体工厂;实现 FactoryMethod 工厂方法创建产品的实例

实现步骤

  1. 创建抽象工厂类,定义具体工厂的公共接口
  2. 创建抽象产品族类,定义抽象产品的公共接口
  3. 创建抽象产品类(继承抽象产品族类),定义具体产品的公共接口
  4. 创建具体产品类(继承抽象产品类),定义生产的具体产品
  5. 创建具体工厂类 (继承抽象工厂类),定义创建对应具体产品实例的方法
  6. 客户端通过实例化具体的工厂类,调用其创建不同目标产品的方法创建不同具体产品

2. 入门案例

小成有两间塑料加工厂,A 厂产容器类,B 厂产模具类。现 A 厂地客户需模具类,B 厂地客户需容器类。冲突是无资源开新分厂。解决方案为在原有两厂增设所需功能,A 厂产容器和模具,B 厂也如此。


UML 设计

画板

代码实现

  1. 创建抽象工厂类,定义具体工厂的公共接口
public abstract class Factory {

    public abstract AbstractProduct manufactureContainer();

    public abstract AbstractProduct manufactureMould();
}
  1. 创建抽象产品族类,定义具体产品的公共接口
public abstract class AbstractProduct {

    public abstract void show();
}
  1. 创建抽象产品类(继承抽象产品族类),定义具体产品的公共接口
public abstract class ContainerProduct extends AbstractProduct {

    @Override
    public abstract void show();
}
public abstract class MouldProduct extends AbstractProduct {

    @Override
    public abstract void show();
}
  1. 创建具体产品类(继承抽象产品类),定义生产的具体产品
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");
    }
}
  1. 创建具体工厂类 (继承抽象工厂类),定义创建对应具体产品实例的方法
//生产抽象产品的 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();
    }
}
  1. 客户端通过实例化具体的工厂类,调用其创建不同目标产品的方法创建不同具体产品
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修饰

  1. 创建抽象工厂类,定义具体工厂的公共接口
//抽象工厂
abstract class AbstractFactory {
     
    //制造抽象产品 Chair
    protected abstract Product manufactureChair();
    
    //制造抽象产品 Sofa
    protected abstract Product manufactureSofa();
   
}
  1. 创建抽象产品族类,定义抽象产品的公共接口
//抽象产品簇
abstract class Product {

    //输出信息
    protected abstract void show();
}
  1. 创建抽象产品类(继承抽象产品族类),定义具体产品的公共接口
abstract class Chair extends Product {

    @Override
    protected abstract void show();
}

abstract class Sofa extends Product {

    @Override
    protected abstract void show();
}
  1. 创建具体产品类(继承抽象产品类),定义生产的具体产品
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");
    }
}
  1. 创建具体工厂类 (继承抽象工厂类),定义创建对应具体产品实例的方法
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();
    }
}
  1. 客户端通过实例化具体的工厂类,调用其创建不同目标产品的方法创建不同具体产品
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. 优缺点

优点

  1. 降低耦合

具体产品类的创建延迟到具体工厂中,这样将对象的创建封装起来;可以减少客户端与具体产品类之间的依赖。从而使得系统耦合度低,这样更加利于后期的维护和扩展。

  1. 更加符合 ⌈开闭 - 原则⌋

新增一种抽象产品类的时候,可以继承抽象产品簇实现抽象部分;如果需要新增一种产品组合,比如上述例子中增加 ⌈简约⌋ 风格的产品组,只需要增加相应的具体产品类和相应的工厂子类。

  1. 符合 ⌈单一职责⌋ 原则

每个具体工厂类只负责创建对应的产品

  1. 方便切换产品簇

客户端可以选用不同的工厂实现,就相当于切换不同的产品簇

缺点

  1. 很难支持为当前产品簇添加一个新的产品

抽象工厂接口中一级确定了可以被创建的产品类型组合(比如:Chair + Sofa)。如果需要添加新的产品则必须要修改抽象工厂的接口,这样就涉及到抽象工厂类和其所有子类具体工厂的改变。

此外,还需要实现抽象产品类和添加具体产品类。

违背了 ⌈开闭原则⌋

对于新的产品簇符合开闭原则;但是对于新的产品种类不符合 ⌈开闭原则⌋

  1. 很容易造成类层次复杂化

5. 提升

进一步认识抽象工厂

定义

提供了一个创建 一系列相关或者 相互依赖 对象的接口,而无需指定它们具体的类

解决思路: 既要创建接口的对象,也需要约束它们之间的关系

工厂方法之间的区别: ⌈工厂模式⌋ 或者 ⌈简单工厂⌋ 关注的是 ⌈单个产品对象的创建⌋

要解决的问题是: 要创建一系列的产品对象,而且这一系列对象是构建新的对象所需要的组成部分,也就是这一系列被创建的对象相互之间要有约束

抽象工厂起约束作用,提供统一外观供客户端使用

当需要切换一个产品簇的时候,只要提供不同的抽象工厂实现就好了,也就是说现在是以产品簇作为一个整体被切换。

思考

本质: 选择产品簇的实现

⌈工厂方法⌋ 是选择单个产品的实现,虽然一个类里面可以由多个工厂方法,但是这些方法之间一般是没有联系的,即使看起来像是有联系

但是 ⌈抽象工厂⌋ 着重的就是为一个产品簇选择实现,定义在抽象工厂里面的方法通常是有联系的,它们都是产品的某一部分或者是相互依赖的。如果抽象工厂里面只定义了一个方法,直接创建产品,那么就退化成为工厂方法了

何时选用抽象工厂模式?

  • 一个系统不要求依赖产品类实例如何创建、组合和表达,而不关系实现的时候
  • 一个系统要由多个产品系列中的一个来配置的时候;可以动态的切换产品簇的时候
  • 系统要求提供一个产品类的库,所有产品以同样的接口出现,客户端类不需要依赖具体实现

比较抽象工厂 VS 工厂模式

  • 工厂方法模式:一般是针对单独的产品对象创建
  • 抽象工厂模式:每个工厂可以创建多种产品

抽象工厂创建的产品簇若简化为只有一个产品,此时抽象工厂与工厂方法类似,抽象工厂模式可退化成工厂方法,工厂方法又能退化成简单工厂,这是它们的联系

在抽象工厂的实现中,还可以使用工厂方法来提供抽象工厂的具体实现,也就是说它们可以组合使用。

抽象方法和单例模式

可以组合使用

在抽象工厂模式里面,具体的工厂实现,在整个应用中,通常一个产品系列只需要一个实例就可以了。因此可以把具体的工厂实现成为单例。

再谈优缺点

优点:

  1. 分离接口和实现
  • 客户端用抽象工厂创建对象,不知具体实现,面向产品接口编程,从具体产品实现中解耦。
  1. 使得切换产品簇容易
  • 因为一个具体的工厂实现代表的一个产品簇;客户端选用不同的工厂实现,就相当于是在切换不同的产品簇
  1. 符合开闭原则 + 单一职责原则 + 基于继承的等级结构
  • 相比于简单工厂模式需要修改工厂类的判断逻辑,新增一种 **产品类 ** 的时候,只需要增加相应的 具体产品类相应的工厂子类 即可
  • 每个具体工厂类只负责创建对应的产品;而简单工厂需要判断生产的产品类
  • 不使用静态工厂方法,可以行程基于继承的等级结构;而简单工厂模式的工厂类使用静态工厂方法

缺点:

  1. 不太容易扩展新的产品

添加新产品组合/集合需 修改抽象工厂逻辑或者修改具体工厂逻辑,会影响所有工厂实现类。虽有可扩展工厂的方式,但不够安全。

  1. 容易造成类层次复杂

抽象工厂实现需分层,每层负责一种选择,屏蔽一种变化,但易造成复杂类层次结构。


参考

  1. 《图解设计模式》 - Factory / Abstract Factory
  2. 抽象工厂-Carson
  3. 抽象工厂方法详解