一、动机
在软件系统中,经常面临”一系列相互依赖的对象“的建构工作,同时,由于需求的变化,往往存在更多系列对象的创建工作。如何应对这种变化? 如何绕过常规的对象创建方法(new
),提供一种”封装机制“来避免客户程序和这种”多系列具体对象创建工作“的紧耦合?
抽象工厂模式是一种创建型设计模式,它能创建一系列相关的对象,而无需指定其具体类。
二、解决方案
首先,抽象工厂模式建议为系列中的每件产品明确声明接口 。然后,确保所有产品变体都继承这些接口。接下来,我们需要声明抽象工厂——包含系列中所有产品构造方法的接口。
那么该如何处理产品变体呢?对于系列产品的每个变体,我们都将基于抽象工厂
接口创建不同的工厂类。每个工厂类都只能返回特定类别的产品。
假设客户端想要工厂创建一把椅子。客户端无需了解工厂类,也不用管工厂类创建出的椅子类型。 无论是现代风格,还是维多利亚风格的椅子,对于客户端来说没有分别,它只需调用抽象椅子
接口就可以了。这样一来,客户端只需知道椅子以某种方式实现了sit
坐下方法就足够了。此外,无论工厂返回的是何种椅子变体,它都会和由同一工厂对象创建的沙发风格一致。
如果客户端仅接触抽象接口,那么谁来创建实际的工厂对象呢? 一般情况下,应用程序会在初始化阶段创建具体工厂对象。而在此之前,应用程序必须根据配置文件或环境设定选择工厂类别。
如果没有应对”多系列对象构建“的需求变化,则没有必要使用Abstract Factory
模式,这时候使用简单的工厂模式完全可以。”系列对象“指的是在某一特定系列下的对象之间有相互依赖,或作用的关系。不同系列的对象之间不能互相依赖。Abstract Factory
模式主要在于应对”新系列“的需求变动。其缺点在于难以应对“新对象”的需求变动。
三、模板模式的结构
- 抽象产品(
Abstract Product
)为构成系列产品的一组不同但相关的产品声明接口。
- 具体产品(
Concrete Product
)是抽象产品的多种不同类型实现。所有变体(维多利亚/现代)都必须实现相应的抽象产品(椅子/沙发)。
- 抽象工厂(
Abstract Factory
)接口声明了一组创建各种抽象产品的方法。
- 具体工厂(
Concrete Factory
)实现抽象工厂的构建方法。每个具体工厂都对应特定产品变体,且仅创建此种产品变体。
- 尽管具体工厂会对具体产品进行初始化,其构建方法签名必须返回相应的抽象产品。这样,使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。客户端(
Client
)只需通过抽象接口调用工厂和产品对象,就能与任何具体工厂/产品变体交互。
四、Abstract Factory
代码示例
假设你正在开发一款家具商店模拟器。你的代码中包括一些类,用于表示:
- 一系列相关产品,例如
椅子Chair
、沙发Sofa
。
- 系列产品的不同变体。例如,你可以使用
现代Modern
、维多利亚Victorian
、装饰风艺术ArtDeco
等风格生成椅子
、沙发
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
| #include <iostream>
class Chair { public: virtual ~Chair() {} virtual void sit() = 0; };
class ArtDecoChair : public Chair { public: void sit() override { std::cout << "ArtDecoChair::sit()" << std::endl; } };
class ModernChair : public Chair { public: void sit() override { std::cout << "ModernChair::sit()" << std::endl; } };
class VictorianChair : public Chair { public: void sit() override { std::cout << "VictorianChair::sit()" << std::endl; } };
class Sofa { public: virtual ~Sofa() {} virtual void lie() = 0; };
class ArtDecoSofa : public Sofa { public: void lie() override { std::cout << "ArtDecoSofa::lie()" << std::endl; } };
class ModernSofa : public Sofa { public: void lie() override { std::cout << "ModernSofa::lie()" << std::endl; } };
class VictorianSofa : public Sofa { public: void lie() override { std::cout << "VictorianSofa::lie()" << std::endl; } };
class AbstractFactory { public: virtual ~AbstractFactory() {} virtual Chair *createChair() = 0; virtual Sofa *createSofa() = 0; };
class ArtDecoFactory : public AbstractFactory { public: Chair *createChair() override { std::cout << "ArtDecoChair created." << std::endl; return new ArtDecoChair; }
Sofa *createSofa() override { std::cout << "ArtDecoSofa created." << std::endl; return new ArtDecoSofa; } };
class ModernFactory : public AbstractFactory { public: Chair *createChair() override { std::cout << "ModernChair created." << std::endl; return new ModernChair; }
Sofa *createSofa() override { std::cout << "ModernSofa created." << std::endl; return new ModernSofa; } };
class VictorianFactory : public AbstractFactory { public: Chair *createChair() override { std::cout << "VictorianChair created." << std::endl; return new VictorianChair; }
Sofa *createSofa() override { std::cout << "VictorianSofa created." << std::endl; return new VictorianSofa; } };
void ClientCode(AbstractFactory &factory) { Chair *chair = factory.createChair(); Sofa *sofa = factory.createSofa(); chair->sit(); sofa->lie();
delete chair; delete sofa; }
int main() { ModernFactory *mf = new ModernFactory; ClientCode(*mf); delete mf;
}
Execution result: ModernChair created. ModernSofa created. ModernChair::sit() ModernSofa::lie()
|
五、使用场景
- 如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息,或者出于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建,在这种情况下,你可以使用抽象工厂。
抽象工厂为你提供了一个接口, 可用于创建每个系列产品的对象。只要代码通过该接口创建对象,那么你就不会生成与应用程序已生成的产品类型不一致的产品。
- 如果你有一个基于一组抽象方法的类,且其主要功能因此变得不明确,那么在这种情况下可以考虑使用抽象工厂模式。
在设计良好的程序中,每个类仅负责一件事。如果一个类与多种类型产品交互,就可以考虑将工厂方法抽取到独立的工厂类或具备完整功能的抽象工厂类中。
六、优缺点
- 你可以确保同一工厂生成的产品相互匹配。
- 你可以避免客户端和具体产品代码的耦合。
- 单一职责原则。你可以将产品生成代码抽取到同一位置,使得代码易于维护。
- 开闭原则。向应用程序中引入新产品变体时,你无需修改客户端代码。
由于采用该模式需要向应用中引入众多接口和类, 代码可能会比之前更加复杂。
七、与其它模式的关系
- 在许多设计工作的初期都会使用工厂方法模式(较为简单,而且可以更方便地通过子类进行定制),随后演化为使用抽象工厂模式、原型模式或生成器模式(更灵活但更加复杂)。
- 生成器重点关注如何分步生成复杂对象。抽象工厂专门用于生产一系列相关对象。抽象工厂会马上返回产品,生成器则允许你在获取产品前执行一些额外构造步骤。
- 抽象工厂模式通常基于一组工厂方法,但你也可以使用原型模式来生成这些类的方法。
- 你可以将抽象工厂和桥接模式搭配使用。如果由桥接定义的抽象只能与特定实现合作,这一模式搭配就非常有用。在这种情况下,抽象工厂可以对这些关系进行封装,并且对客户端代码隐藏其复杂性。
- 抽象工厂、生成器和原型都可以用单例模式来实现。