抽象工厂(Abstract Factory)——对象创建型模式

163 阅读4分钟

前言

在之前提到的工厂方法中,每一个具体的工厂对应一个具体的产品,但是我们有时需要一个工厂可以提供多个产品对象,此时可以使用抽象工厂模式来完成。在搞清抽象工厂之前,先来搞明白产品等级结构以及产品族的概念

  • 产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是手机,其子类包括苹果手机、三星手机、华为手机,则抽象的手机类与具体的手机之间构成了一个产品等级结构。抽象手机是父类,具体的手机是子类
  • 产品族:在抽象工厂模式中,产品族是指由同一个工厂生产的,但是位于不同产品等级结构中的一组产品,比如说苹果公司的苹果手机、苹果平板电脑,苹果手机位于手机的产品等级结构中而苹果平板电脑位于电脑的等级结构中

模式意图

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

模式结构

image.png

代码示例

// 第一个产品的接口
abstract class Weapon {
    abstract void attack();
}
// 另一个产品的接口
abstract class Food {
    abstract void eat();
}

// 抽象工厂
// 接口定义一组返回不同抽象产品的方法,这些产品可以称之为产品族
public abstract class AbstractFactory {
    abstract Weapon createWeapon();
    abstract Food createFood();
}

class WeaponA extends Weapon {

    @Override
    void attack() {
        System.out.println("attack A");
    }
}

class FoodA extends Food {

    @Override
    void eat() {
        System.out.println("eat A");
    }
}

// 具体工厂
// 返回具体产品
class AbstractFactoryA extends AbstractFactory {

    @Override
    Weapon createWeapon() {
        return new WeaponA();
    }

    @Override
    Food createFood() {
        return new FoodA();
    }
}

适用场景

  1. 系统要独立于它的产品的创建、组合和表示(不依赖于具体类)
  2. 系统中有多于一个的产品族,而每次只使用其中一个产品族
  3. 要强调一个产品族的产品对象的设计以便联合使用
  4. 系统提供一个产品类的库,所有的产品都必须以相同的接口出现,从而使客户端不依赖具体的实现

常见案例

  1. 软件系统中更换界面主题

模式效果(优缺点)

  1. 避免客户端和具体产品的代码耦合(因为一个工厂封装了产品对象创建的过程,客户在使用的时候只需要关注具体工厂而不用关注产品相关的类)
  2. 易于交换产品族(由于一个产品族的内产品对象遵循同一接口,因此进行产品族的替换是一个非常简单的操作——只需要修改具体的产品工厂即可)
  3. 有利于产品的一致性(当一个产品族的产品被设计在一起工作时,一个应用每次必须使用该产品族的对象,不会出现任何差异)
  4. 难以扩展新的产品等级结构(由于 AbstractFactory已经确定了可以被创建的产品集合,支持新的此产品就需要扩展该接口,这样一来所有的具体工厂子类都需要进行改变)

模式扩展

  1. 工厂方法针对的是一个产品等级结构而抽象工厂针对的是多个产品等级结构
  2. 抽象工厂模式中每一个具体工厂类只负责创建一个产品对象时,抽象工厂模式就可以叫做工厂方法模式
  3. 许多设计初期都会使用工厂方法模式,随后演化为使用抽象工厂原型或者生成器模式
  4. 抽象工厂通常基于一组工厂方法,但是你也可以使用原型模式来生成这些类的方法(抽象工厂往往和原型模式是相互竞争的,但是也可以一起使用,比如说,抽象工厂可以存储一个被克隆的原型的集合,就像原型注册表一样,并且返回产品对象)
  5. 抽象工厂重点在于生成一系列相关对象;生成器模式重点在于如何分步生成复杂对象。抽象工厂会马上返回产品对象;生成器模式则允许你在获取产品前执行一些额外构造步骤
  6. 抽象工厂生成器原型模式都可以通常单例模式来实现
  7. 当只需要对客户端代码隐藏子系统创建对象的方式时,可以使用抽象工厂来代替外观
  8. 可以将抽象工厂桥接模式搭配使用。如果由桥接定义的抽象只能与特定实现合作,这一模式搭配就非常有用。在这种情况下,抽象工厂可以对这些关系进行封装,并对客户端代码隐藏复杂性