https://refactoringguru.cn/design-patterns/abstract-factory

创建型模式-抽象工厂模式

亦称: Abstract Factory

意图

抽象工厂模式是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。

问题

假设你正在开发一款家具商店模拟器。 你的代码中包括一些类, 用于表示:

  1. 一系列相关产品, 例如 椅子Chair 、 沙发Sofa和 咖啡桌Coffee­Table 。
  2. 系列产品的不同变体。 例如, 你可以使用 现代Modern 、 维多利亚Victorian 、 装饰风艺术Art­Deco等风格生成 椅子沙发咖啡桌

系列产品及其不同变体。

你需要设法单独生成每件家具对象, 这样才能确保其风格一致。 如果顾客收到的家具风格不一样, 他们可不会开心。

现代风格的沙发和维多利亚风格的椅子不搭。

此外, 你也不希望在添加新产品或新风格时修改已有代码。 家具供应商对于产品目录的更新非常频繁, 你不会想在每次更新时都去修改核心代码的。

解决方案

首先, 抽象工厂模式建议为系列中的每件产品明确声明接口 (例如椅子、 沙发或咖啡桌)。 然后, 确保所有产品变体都继承这些接口。 例如, 所有风格的椅子都实现 椅子接口; 所有风格的咖啡桌都实现 咖啡桌接口, 以此类推。

同一对象的所有变体都必须放置在同一个类层次结构之中。

接下来, 我们需要声明抽象工厂——包含系列中所有产品构造方法的接口。 例如 create­Chair创建椅子 、 create­Sofa创建沙发和 create­Coffee­Table创建咖啡桌 。 这些方法必须返回抽象产品类型, 即我们之前抽取的那些接口: 椅子沙发咖啡桌等等。

每个具体工厂类都对应一个特定的产品变体。

那么该如何处理产品变体呢? 对于系列产品的每个变体, 我们都将基于 抽象工厂接口创建不同的工厂类。 每个工厂类都只能返回特定类别的产品, 例如, 现代家具工厂Modern­Furniture­Factory只能创建 现代椅子Modern­Chair 、 现代沙发Modern­Sofa和 现代咖啡桌Modern­Coffee­Table对象。

客户端代码可以通过相应的抽象接口调用工厂和产品类。 你无需修改实际客户端代码, 就能更改传递给客户端的工厂类, 也能更改客户端代码接收的产品变体。

客户端无需了解其所调用工厂的具体类信息。

假设客户端想要工厂创建一把椅子。 客户端无需了解工厂类, 也不用管工厂类创建出的椅子类型。 无论是现代风格, 还是维多利亚风格的椅子, 对于客户端来说没有分别, 它只需调用抽象 椅子接口就可以了。 这样一来, 客户端只需知道椅子以某种方式实现了 sit­On坐下方法就足够了。 此外, 无论工厂返回的是何种椅子变体, 它都会和由同一工厂对象创建的沙发或咖啡桌风格一致。

最后一点说明: 如果客户端仅接触抽象接口, 那么谁来创建实际的工厂对象呢? 一般情况下, 应用程序会在初始化阶段创建具体工厂对象。 而在此之前, 应用程序必须根据配置文件或环境设定选择工厂类别。

抽象工厂模式结构

  1. 抽象产品 (Abstract Product) 为构成系列产品的一组不同但相关的产品声明接口。
  2. 具体产品 (Concrete Product) 是抽象产品的多种不同类型实现。 所有变体 (维多利亚/现代) 都必须实现相应的抽象产品 (椅子/沙发)。
  3. 抽象工厂 (Abstract Factory) 接口声明了一组创建各种抽象产品的方法。
  4. 具体工厂 (Concrete Factory) 实现抽象工厂的构建方法。 每个具体工厂都对应特定产品变体, 且仅创建此种产品变体。
  5. 尽管具体工厂会对具体产品进行初始化, 其构建方法签名必须返回相应的抽象产品。 这样, 使用工厂类的客户端代码就不会与工厂创建的特定产品变体耦合。 客户端 (Client) 只需通过抽象接口调用工厂和产品对象, 就能与任何具体工厂/产品变体交互。

伪代码

下面例子通过应用抽象工厂模式, 使得客户端代码无需与具体 UI 类耦合, 就能创建跨平台的 UI 元素, 同时确保所创建的元素与指定的操作系统匹配。

跨平台 UI 类示例。

跨平台应用中的相同 UI 元素功能类似, 但是在不同操作系统下的外观有一定差异。 此外, 你需要确保 UI 元素与当前操作系统风格一致。 你一定不希望在 Windows 系统下运行的应用程序中显示 macOS 的控件。

抽象工厂接口声明一系列构建方法, 客户端代码可调用它们生成不同风格的 UI 元素。 每个具体工厂对应特定操作系统, 并负责生成符合该操作系统风格的 UI 元素。

其运作方式如下: 应用程序启动后检测当前操作系统。 根据该信息, 应用程序通过与该操作系统对应的类创建工厂对象。 其余代码使用该工厂对象创建 UI 元素。 这样可以避免生成错误类型的元素。

使用这种方法, 客户端代码只需调用抽象接口, 而无需了解具体工厂类和 UI 元素。 此外, 客户端代码还支持未来添加新的工厂或 UI 元素。

这样一来, 每次在应用程序中添加新的 UI 元素变体时, 你都无需修改客户端代码。 你只需创建一个能够生成这些 UI 元素的工厂类, 然后稍微修改应用程序的初始代码, 使其能够选择合适的工厂类即可。

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
// 抽象工厂接口声明了一组能返回不同抽象产品的方法。这些产品属于同一个系列
// 且在高层主题或概念上具有相关性。同系列的产品通常能相互搭配使用。系列产
// 品可有多个变体,但不同变体的产品不能搭配使用。
interface GUIFactory is
method createButton():Button
method createCheckbox():Checkbox


// 具体工厂可生成属于同一变体的系列产品。工厂会确保其创建的产品能相互搭配
// 使用。具体工厂方法签名会返回一个抽象产品,但在方法内部则会对具体产品进
// 行实例化。
class WinFactory implements GUIFactory is
method createButton():Button is
return new WinButton()
method createCheckbox():Checkbox is
return new WinCheckbox()

// 每个具体工厂中都会包含一个相应的产品变体。
class MacFactory implements GUIFactory is
method createButton():Button is
return new MacButton()
method createCheckbox():Checkbox is
return new MacCheckbox()


// 系列产品中的特定产品必须有一个基础接口。所有产品变体都必须实现这个接口。
interface Button is
method paint()

// 具体产品由相应的具体工厂创建。
class WinButton implements Button is
method paint() is
// 根据 Windows 样式渲染按钮。

class MacButton implements Button is
method paint() is
// 根据 macOS 样式渲染按钮

// 这是另一个产品的基础接口。所有产品都可以互动,但是只有相同具体变体的产
// 品之间才能够正确地进行交互。
interface Checkbox is
method paint()

class WinCheckbox implements Checkbox is
method paint() is
// 根据 Windows 样式渲染复选框。

class MacCheckbox implements Checkbox is
method paint() is
// 根据 macOS 样式渲染复选框。

// 客户端代码仅通过抽象类型(GUIFactory、Button 和 Checkbox)使用工厂
// 和产品。这让你无需修改任何工厂或产品子类就能将其传递给客户端代码。
class Application is
private field factory: GUIFactory
private field button: Button
constructor Application(factory: GUIFactory) is
this.factory = factory
method createUI() is
this.button = factory.createButton()
method paint() is
button.paint()


// 程序会根据当前配置或环境设定选择工厂类型,并在运行时创建工厂(通常在初
// 始化阶段)。
class ApplicationConfigurator is
method main() is
config = readApplicationConfigFile()

if (config.OS == "Windows") then
factory = new WinFactory()
else if (config.OS == "Mac") then
factory = new MacFactory()
else
throw new Exception("错误!未知的操作系统。")

Application app = new Application(factory)

抽象工厂模式适合应用场景

如果代码需要与多个不同系列的相关产品交互,但是由于无法提前获取相关信息,或者出于对未来扩展性的考虑,你不希望代码基于产品的具体类进行构建,在这种情况下,你可以使用抽象工厂。

抽象工厂为你提供了一个接口, 可用于创建每个系列产品的对象。 只要代码通过该接口创建对象, 那么你就不会生成与应用程序已生成的产品类型不一致的产品。

如果你有一个基于一组抽象方法的类,且其主要功能因此变得不明确,那么在这种情况下可以考虑使用抽象工厂模式。

在设计良好的程序中, 每个类仅负责一件事。 如果一个类与多种类型产品交互, 就可以考虑将工厂方法抽取到独立的工厂类或具备完整功能的抽象工厂类中。

实现方式

  1. 以不同的产品类型与产品变体为维度绘制矩阵。
  2. 为所有产品声明抽象产品接口。 然后让所有具体产品类实现这些接口。
  3. 声明抽象工厂接口, 并且在接口中为所有抽象产品提供一组构建方法。
  4. 为每种产品变体实现一个具体工厂类。
  5. 在应用程序中开发初始化代码。 该代码根据应用程序配置或当前环境, 对特定具体工厂类进行初始化。 然后将该工厂对象传递给所有需要创建产品的类。
  6. 找出代码中所有对产品构造函数的直接调用, 将其替换为对工厂对象中相应构建方法的调用。

抽象工厂模式优缺点

优点

  • 你可以确保同一工厂生成的产品相互匹配。
  • 你可以避免客户端和具体产品代码的耦合。
  • 单一职责原则。 你可以将产品生成代码抽取到同一位置, 使得代码易于维护。
  • 开闭原则。 向应用程序中引入新产品变体时, 你无需修改客户端代码。

缺点

  • 由于采用该模式需要向应用中引入众多接口和类, 代码可能会比之前更加复杂。

与其他模式的关系

代码示例

抽象工厂是一种创建型设计模式, 它能创建一系列相关的对象, 而无需指定其具体类。

抽象工厂定义了用于创建不同产品的接口, 但将实际的创建工作留给了具体工厂类。 每个工厂类型都对应一个特定的产品变体。

在创建产品时, 客户端代码调用的是工厂对象的构建方法, 而不是直接调用构造函数 ( new操作符)。 由于一个工厂对应一种产品变体, 因此它创建的所有产品都可相互兼容。

客户端代码仅通过其抽象接口与工厂和产品进行交互。 该接口允许同一客户端代码与不同产品进行交互。 你只需创建一个具体工厂类并将其传递给客户端代码即可。

在 C# 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 抽象工厂模式在 C# 代码中很常见。 许多框架和程序库会将它作为扩展和自定义其标准组件的一种方式。

识别方法: 我们可以通过方法来识别该模式——其会返回一个工厂对象。 接下来, 工厂将被用于创建特定的子组件。

概念示例

本例说明了抽象工厂设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

Program.cs: 概念示例

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
157
158
using System;

namespace RefactoringGuru.DesignPatterns.AbstractFactory.Conceptual
{
// The Abstract Factory interface declares a set of methods that return
// different abstract products. These products are called a family and are
// related by a high-level theme or concept. Products of one family are
// usually able to collaborate among themselves. A family of products may
// have several variants, but the products of one variant are incompatible
// with products of another.
public interface IAbstractFactory
{
IAbstractProductA CreateProductA();

IAbstractProductB CreateProductB();
}

// Concrete Factories produce a family of products that belong to a single
// variant. The factory guarantees that resulting products are compatible.
// Note that signatures of the Concrete Factory's methods return an abstract
// product, while inside the method a concrete product is instantiated.
class ConcreteFactory1 : IAbstractFactory
{
public IAbstractProductA CreateProductA()
{
return new ConcreteProductA1();
}

public IAbstractProductB CreateProductB()
{
return new ConcreteProductB1();
}
}

// Each Concrete Factory has a corresponding product variant.
class ConcreteFactory2 : IAbstractFactory
{
public IAbstractProductA CreateProductA()
{
return new ConcreteProductA2();
}

public IAbstractProductB CreateProductB()
{
return new ConcreteProductB2();
}
}

// Each distinct product of a product family should have a base interface.
// All variants of the product must implement this interface.
public interface IAbstractProductA
{
string UsefulFunctionA();
}

// Concrete Products are created by corresponding Concrete Factories.
class ConcreteProductA1 : IAbstractProductA
{
public string UsefulFunctionA()
{
return "The result of the product A1.";
}
}

class ConcreteProductA2 : IAbstractProductA
{
public string UsefulFunctionA()
{
return "The result of the product A2.";
}
}

// Here's the the base interface of another product. All products can
// interact with each other, but proper interaction is possible only between
// products of the same concrete variant.
public interface IAbstractProductB
{
// Product B is able to do its own thing...
string UsefulFunctionB();

// ...but it also can collaborate with the ProductA.
//
// The Abstract Factory makes sure that all products it creates are of
// the same variant and thus, compatible.
string AnotherUsefulFunctionB(IAbstractProductA collaborator);
}

// Concrete Products are created by corresponding Concrete Factories.
class ConcreteProductB1 : IAbstractProductB
{
public string UsefulFunctionB()
{
return "The result of the product B1.";
}

// The variant, Product B1, is only able to work correctly with the
// variant, Product A1. Nevertheless, it accepts any instance of
// AbstractProductA as an argument.
public string AnotherUsefulFunctionB(IAbstractProductA collaborator)
{
var result = collaborator.UsefulFunctionA();

return $"The result of the B1 collaborating with the ({result})";
}
}

class ConcreteProductB2 : IAbstractProductB
{
public string UsefulFunctionB()
{
return "The result of the product B2.";
}

// The variant, Product B2, is only able to work correctly with the
// variant, Product A2. Nevertheless, it accepts any instance of
// AbstractProductA as an argument.
public string AnotherUsefulFunctionB(IAbstractProductA collaborator)
{
var result = collaborator.UsefulFunctionA();

return $"The result of the B2 collaborating with the ({result})";
}
}

// The client code works with factories and products only through abstract
// types: AbstractFactory and AbstractProduct. This lets you pass any
// factory or product subclass to the client code without breaking it.
class Client
{
public void Main()
{
// The client code can work with any concrete factory class.
Console.WriteLine("Client: Testing client code with the first factory type...");
ClientMethod(new ConcreteFactory1());
Console.WriteLine();

Console.WriteLine("Client: Testing the same client code with the second factory type...");
ClientMethod(new ConcreteFactory2());
}

public void ClientMethod(IAbstractFactory factory)
{
var productA = factory.CreateProductA();
var productB = factory.CreateProductB();

Console.WriteLine(productB.UsefulFunctionB());
Console.WriteLine(productB.AnotherUsefulFunctionB(productA));
}
}

class Program
{
static void Main(string[] args)
{
new Client().Main();
}
}
}

Output.txt: 执行结果

1
2
3
4
5
6
7
Client: Testing client code with the first factory type...
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)

Client: Testing the same client code with the second factory type...
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)

在 C++ 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 抽象工厂模式在 C++ 代码中很常见。 许多框架和程序库会将它作为扩展和自定义其标准组件的一种方式。

识别方法: 我们可以通过方法来识别该模式——其会返回一个工厂对象。 接下来, 工厂将被用于创建特定的子组件。

概念示例

本例说明了抽象工厂设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

main.cc: 概念示例

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
/**
* Each distinct product of a product family should have a base interface. All
* variants of the product must implement this interface.
*/
class AbstractProductA {
public:
virtual ~AbstractProductA(){};
virtual std::string UsefulFunctionA() const = 0;
};

/**
* Concrete Products are created by corresponding Concrete Factories.
*/
class ConcreteProductA1 : public AbstractProductA {
public:
std::string UsefulFunctionA() const override {
return "The result of the product A1.";
}
};

class ConcreteProductA2 : public AbstractProductA {
std::string UsefulFunctionA() const override {
return "The result of the product A2.";
}
};

/**
* Here's the the base interface of another product. All products can interact
* with each other, but proper interaction is possible only between products of
* the same concrete variant.
*/
class AbstractProductB {
/**
* Product B is able to do its own thing...
*/
public:
virtual ~AbstractProductB(){};
virtual std::string UsefulFunctionB() const = 0;
/**
* ...but it also can collaborate with the ProductA.
*
* The Abstract Factory makes sure that all products it creates are of the
* same variant and thus, compatible.
*/
virtual std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const = 0;
};

/**
* Concrete Products are created by corresponding Concrete Factories.
*/
class ConcreteProductB1 : public AbstractProductB {
public:
std::string UsefulFunctionB() const override {
return "The result of the product B1.";
}
/**
* The variant, Product B1, is only able to work correctly with the variant,
* Product A1. Nevertheless, it accepts any instance of AbstractProductA as an
* argument.
*/
std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const override {
const std::string result = collaborator.UsefulFunctionA();
return "The result of the B1 collaborating with ( " + result + " )";
}
};

class ConcreteProductB2 : public AbstractProductB {
public:
std::string UsefulFunctionB() const override {
return "The result of the product B2.";
}
/**
* The variant, Product B2, is only able to work correctly with the variant,
* Product A2. Nevertheless, it accepts any instance of AbstractProductA as an
* argument.
*/
std::string AnotherUsefulFunctionB(const AbstractProductA &collaborator) const override {
const std::string result = collaborator.UsefulFunctionA();
return "The result of the B2 collaborating with ( " + result + " )";
}
};

/**
* The Abstract Factory interface declares a set of methods that return
* different abstract products. These products are called a family and are
* related by a high-level theme or concept. Products of one family are usually
* able to collaborate among themselves. A family of products may have several
* variants, but the products of one variant are incompatible with products of
* another.
*/
class AbstractFactory {
public:
virtual AbstractProductA *CreateProductA() const = 0;
virtual AbstractProductB *CreateProductB() const = 0;
};

/**
* Concrete Factories produce a family of products that belong to a single
* variant. The factory guarantees that resulting products are compatible. Note
* that signatures of the Concrete Factory's methods return an abstract product,
* while inside the method a concrete product is instantiated.
*/
class ConcreteFactory1 : public AbstractFactory {
public:
AbstractProductA *CreateProductA() const override {
return new ConcreteProductA1();
}
AbstractProductB *CreateProductB() const override {
return new ConcreteProductB1();
}
};

/**
* Each Concrete Factory has a corresponding product variant.
*/
class ConcreteFactory2 : public AbstractFactory {
public:
AbstractProductA *CreateProductA() const override {
return new ConcreteProductA2();
}
AbstractProductB *CreateProductB() const override {
return new ConcreteProductB2();
}
};

/**
* The client code works with factories and products only through abstract
* types: AbstractFactory and AbstractProduct. This lets you pass any factory or
* product subclass to the client code without breaking it.
*/

void ClientCode(const AbstractFactory &factory) {
const AbstractProductA *product_a = factory.CreateProductA();
const AbstractProductB *product_b = factory.CreateProductB();
std::cout << product_b->UsefulFunctionB() << "\n";
std::cout << product_b->AnotherUsefulFunctionB(*product_a) << "\n";
delete product_a;
delete product_b;
}

int main() {
std::cout << "Client: Testing client code with the first factory type:\n";
ConcreteFactory1 *f1 = new ConcreteFactory1();
ClientCode(*f1);
delete f1;
std::cout << std::endl;
std::cout << "Client: Testing the same client code with the second factory type:\n";
ConcreteFactory2 *f2 = new ConcreteFactory2();
ClientCode(*f2);
delete f2;
return 0;
}

Output.txt: 执行结果

1
2
3
4
5
6
7
Client: Testing client code with the first factory type:
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)

Client: Testing the same client code with the second factory type:
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)

在 Java 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 抽象工厂模式在 Java 代码中很常见。 许多框架和程序库会将它作为扩展和自定义其标准组件的一种方式。

以下是来自核心 Java 程序库的一些示例:

识别方法: 我们可以通过方法来识别该模式——其会返回一个工厂对象。 接下来, 工厂将被用于创建特定的子组件。

跨平台 GUI 组件系列及其创建方式

在本例中, 按钮和复选框将被作为产品。 它们有两个变体: macOS 版和 Windows 版。

抽象工厂定义了用于创建按钮和复选框的接口。 而两个具体工厂都会返回同一变体的两个产品。

客户端代码使用抽象接口与工厂和产品进行交互。 同样的代码能与依赖于不同工厂对象类型的多种产品变体进行交互。

buttons: 第一个产品层次结构

buttons/Button.java

1
2
3
4
5
6
7
8
9
10
11
12
package refactoring_guru.abstract_factory.example.buttons;

/**
* Abstract Factory assumes that you have several families of products,
* structured into separate class hierarchies (Button/Checkbox). All products of
* the same family have the common interface.
*
* This is the common interface for buttons family.
*/
public interface Button {
void paint();
}

buttons/MacOSButton.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package refactoring_guru.abstract_factory.example.buttons;

/**
* All products families have the same varieties (MacOS/Windows).
*
* This is a MacOS variant of a button.
*/
public class MacOSButton implements Button {

@Override
public void paint() {
System.out.println("You have created MacOSButton.");
}
}

buttons/WindowsButton.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package refactoring_guru.abstract_factory.example.buttons;

/**
* All products families have the same varieties (MacOS/Windows).
*
* This is another variant of a button.
*/
public class WindowsButton implements Button {

@Override
public void paint() {
System.out.println("You have created WindowsButton.");
}
}

checkboxes: 第二个产品层次结构

checkboxes/Checkbox.java

1
2
3
4
5
6
7
8
package refactoring_guru.abstract_factory.example.checkboxes;

/**
* Checkboxes is the second product family. It has the same variants as buttons.
*/
public interface Checkbox {
void paint();
}

checkboxes/MacOSCheckbox.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package refactoring_guru.abstract_factory.example.checkboxes;

/**
* All products families have the same varieties (MacOS/Windows).
*
* This is a variant of a checkbox.
*/
public class MacOSCheckbox implements Checkbox {

@Override
public void paint() {
System.out.println("You have created MacOSCheckbox.");
}
}

checkboxes/WindowsCheckbox.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package refactoring_guru.abstract_factory.example.checkboxes;

/**
* All products families have the same varieties (MacOS/Windows).
*
* This is another variant of a checkbox.
*/
public class WindowsCheckbox implements Checkbox {

@Override
public void paint() {
System.out.println("You have created WindowsCheckbox.");
}
}

factories

factories/GUIFactory.java: 抽象工厂

1
2
3
4
5
6
7
8
9
10
11
12
package refactoring_guru.abstract_factory.example.factories;

import refactoring_guru.abstract_factory.example.buttons.Button;
import refactoring_guru.abstract_factory.example.checkboxes.Checkbox;

/**
* Abstract factory knows about all (abstract) product types.
*/
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}

factories/MacOSFactory.java: 具体工厂 ( mac­OS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package refactoring_guru.abstract_factory.example.factories;

import refactoring_guru.abstract_factory.example.buttons.Button;
import refactoring_guru.abstract_factory.example.buttons.MacOSButton;
import refactoring_guru.abstract_factory.example.checkboxes.Checkbox;
import refactoring_guru.abstract_factory.example.checkboxes.MacOSCheckbox;

/**
* Each concrete factory extends basic factory and responsible for creating
* products of a single variety.
*/
public class MacOSFactory implements GUIFactory {

@Override
public Button createButton() {
return new MacOSButton();
}

@Override
public Checkbox createCheckbox() {
return new MacOSCheckbox();
}
}

factories/WindowsFactory.java: 具体工厂 (Windows)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package refactoring_guru.abstract_factory.example.factories;

import refactoring_guru.abstract_factory.example.buttons.Button;
import refactoring_guru.abstract_factory.example.buttons.WindowsButton;
import refactoring_guru.abstract_factory.example.checkboxes.Checkbox;
import refactoring_guru.abstract_factory.example.checkboxes.WindowsCheckbox;

/**
* Each concrete factory extends basic factory and responsible for creating
* products of a single variety.
*/
public class WindowsFactory implements GUIFactory {

@Override
public Button createButton() {
return new WindowsButton();
}

@Override
public Checkbox createCheckbox() {
return new WindowsCheckbox();
}
}

app

app/Application.java: 客户端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package refactoring_guru.abstract_factory.example.app;

import refactoring_guru.abstract_factory.example.buttons.Button;
import refactoring_guru.abstract_factory.example.checkboxes.Checkbox;
import refactoring_guru.abstract_factory.example.factories.GUIFactory;

/**
* Factory users don't care which concrete factory they use since they work with
* factories and products through abstract interfaces.
*/
public class Application {
private Button button;
private Checkbox checkbox;

public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}

public void paint() {
button.paint();
checkbox.paint();
}
}

Demo.java: 程序配置

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
package refactoring_guru.abstract_factory.example;

import refactoring_guru.abstract_factory.example.app.Application;
import refactoring_guru.abstract_factory.example.factories.GUIFactory;
import refactoring_guru.abstract_factory.example.factories.MacOSFactory;
import refactoring_guru.abstract_factory.example.factories.WindowsFactory;

/**
* Demo class. Everything comes together here.
*/
public class Demo {

/**
* Application picks the factory type and creates it in run time (usually at
* initialization stage), depending on the configuration or environment
* variables.
*/
private static Application configureApplication() {
Application app;
GUIFactory factory;
String osName = System.getProperty("os.name").toLowerCase();
if (osName.contains("mac")) {
factory = new MacOSFactory();
app = new Application(factory);
} else {
factory = new WindowsFactory();
app = new Application(factory);
}
return app;
}

public static void main(String[] args) {
Application app = configureApplication();
app.paint();
}
}

OutputDemo.txt: 执行结果

1
2
You create WindowsButton.
You created WindowsCheckbox.

在 PHP 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 抽象工厂模式在 PHP 代码中很常见。 许多框架和程序库将它作为扩展和自定义其标准组件的一种方式。

识别方法: 我们可以通过方法来识别该模式——其会返回一个工厂对象。 接下来, 工厂将被用于创建特定的子组件。

概念示例

本例说明了抽象工厂设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各元素会以何种方式相互关联?

了解该模式的结构后, 你可以更容易地理解下面的基于实际情况的 PHP 应用案例。

index.php: 概念示例

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
<?php

namespace RefactoringGuru\AbstractFactory\Conceptual;

/**
* The Abstract Factory interface declares a set of methods that return
* different abstract products. These products are called a family and are
* related by a high-level theme or concept. Products of one family are usually
* able to collaborate among themselves. A family of products may have several
* variants, but the products of one variant are incompatible with products of
* another.
*/
interface AbstractFactory
{
public function createProductA(): AbstractProductA;

public function createProductB(): AbstractProductB;
}

/**
* Concrete Factories produce a family of products that belong to a single
* variant. The factory guarantees that resulting products are compatible. Note
* that signatures of the Concrete Factory's methods return an abstract product,
* while inside the method a concrete product is instantiated.
*/
class ConcreteFactory1 implements AbstractFactory
{
public function createProductA(): AbstractProductA
{
return new ConcreteProductA1();
}

public function createProductB(): AbstractProductB
{
return new ConcreteProductB1();
}
}

/**
* Each Concrete Factory has a corresponding product variant.
*/
class ConcreteFactory2 implements AbstractFactory
{
public function createProductA(): AbstractProductA
{
return new ConcreteProductA2();
}

public function createProductB(): AbstractProductB
{
return new ConcreteProductB2();
}
}

/**
* Each distinct product of a product family should have a base interface. All
* variants of the product must implement this interface.
*/
interface AbstractProductA
{
public function usefulFunctionA(): string;
}

/**
* Concrete Products are created by corresponding Concrete Factories.
*/
class ConcreteProductA1 implements AbstractProductA
{
public function usefulFunctionA(): string
{
return "The result of the product A1.";
}
}

class ConcreteProductA2 implements AbstractProductA
{
public function usefulFunctionA(): string
{
return "The result of the product A2.";
}
}

/**
* Here's the the base interface of another product. All products can interact
* with each other, but proper interaction is possible only between products of
* the same concrete variant.
*/
interface AbstractProductB
{
/**
* Product B is able to do its own thing...
*/
public function usefulFunctionB(): string;

/**
* ...but it also can collaborate with the ProductA.
*
* The Abstract Factory makes sure that all products it creates are of the
* same variant and thus, compatible.
*/
public function anotherUsefulFunctionB(AbstractProductA $collaborator): string;
}

/**
* Concrete Products are created by corresponding Concrete Factories.
*/
class ConcreteProductB1 implements AbstractProductB
{
public function usefulFunctionB(): string
{
return "The result of the product B1.";
}

/**
* The variant, Product B1, is only able to work correctly with the variant,
* Product A1. Nevertheless, it accepts any instance of AbstractProductA as
* an argument.
*/
public function anotherUsefulFunctionB(AbstractProductA $collaborator): string
{
$result = $collaborator->usefulFunctionA();

return "The result of the B1 collaborating with the ({$result})";
}
}

class ConcreteProductB2 implements AbstractProductB
{
public function usefulFunctionB(): string
{
return "The result of the product B2.";
}

/**
* The variant, Product B2, is only able to work correctly with the variant,
* Product A2. Nevertheless, it accepts any instance of AbstractProductA as
* an argument.
*/
public function anotherUsefulFunctionB(AbstractProductA $collaborator): string
{
$result = $collaborator->usefulFunctionA();

return "The result of the B2 collaborating with the ({$result})";
}
}

/**
* The client code works with factories and products only through abstract
* types: AbstractFactory and AbstractProduct. This lets you pass any factory or
* product subclass to the client code without breaking it.
*/
function clientCode(AbstractFactory $factory)
{
$productA = $factory->createProductA();
$productB = $factory->createProductB();

echo $productB->usefulFunctionB() . "\n";
echo $productB->anotherUsefulFunctionB($productA) . "\n";
}

/**
* The client code can work with any concrete factory class.
*/
echo "Client: Testing client code with the first factory type:\n";
clientCode(new ConcreteFactory1());

echo "\n";

echo "Client: Testing the same client code with the second factory type:\n";
clientCode(new ConcreteFactory2());

Output.txt: 执行结果

1
2
3
4
5
6
7
Client: Testing client code with the first factory type:
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)

Client: Testing the same client code with the second factory type:
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)

真实世界示例

本例中, 抽象工厂模式提供了一个框架, 以为网页中的各种元素创建不同类型的模板。

只要网络应用的类独立于具体的渲染引擎类, 它就能同时支持不同的渲染引擎。 因此, 应用的对象仅能通过模板对象的抽象接口与其进行互动。 你的代码不能直接创建模板对象, 而是应该将创建行为委派给特殊的工厂对象。 最后, 你的代码也不能依赖于工厂对象, 而是应该通过抽象工厂接口与其进行交互。

你将能为应用中的每个渲染引擎提供相应的工厂对象。 应用中的所有模板都将由该工厂创建, 而且模板与工厂的类型相匹配。 如果你决定更改渲染引擎, 可将一个新的工厂对象传递给客户端代码, 无需对已有代码进行任何修改。

index.php: 真实世界示例

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
<?php

namespace RefactoringGuru\AbstractFactory\RealWorld;

/**
* The Abstract Factory interface declares creation methods for each distinct
* product type.
*/
interface TemplateFactory
{
public function createTitleTemplate(): TitleTemplate;

public function createPageTemplate(): PageTemplate;

public function getRenderer(): TemplateRenderer;
}

/**
* Each Concrete Factory corresponds to a specific variant (or family) of
* products.
*
* This Concrete Factory creates Twig templates.
*/
class TwigTemplateFactory implements TemplateFactory
{
public function createTitleTemplate(): TitleTemplate
{
return new TwigTitleTemplate();
}

public function createPageTemplate(): PageTemplate
{
return new TwigPageTemplate($this->createTitleTemplate());
}

public function getRenderer(): TemplateRenderer
{
return new TwigRenderer();
}
}

/**
* And this Concrete Factory creates PHPTemplate templates.
*/
class PHPTemplateFactory implements TemplateFactory
{
public function createTitleTemplate(): TitleTemplate
{
return new PHPTemplateTitleTemplate();
}

public function createPageTemplate(): PageTemplate
{
return new PHPTemplatePageTemplate($this->createTitleTemplate());
}

public function getRenderer(): TemplateRenderer
{
return new PHPTemplateRenderer();
}
}

/**
* Each distinct product type should have a separate interface. All variants of
* the product must follow the same interface.
*
* For instance, this Abstract Product interface describes the behavior of page
* title templates.
*/
interface TitleTemplate
{
public function getTemplateString(): string;
}

/**
* This Concrete Product provides Twig page title templates.
*/
class TwigTitleTemplate implements TitleTemplate
{
public function getTemplateString(): string
{
return "<h1>{{ title }}</h1>";
}
}

/**
* And this Concrete Product provides PHPTemplate page title templates.
*/
class PHPTemplateTitleTemplate implements TitleTemplate
{
public function getTemplateString(): string
{
return "<h1><?= \$title; ?></h1>";
}
}

/**
* This is another Abstract Product type, which describes whole page templates.
*/
interface PageTemplate
{
public function getTemplateString(): string;
}

/**
* The page template uses the title sub-template, so we have to provide the way
* to set it in the sub-template object. The abstract factory will link the page
* template with a title template of the same variant.
*/
abstract class BasePageTemplate implements PageTemplate
{
protected $titleTemplate;

public function __construct(TitleTemplate $titleTemplate)
{
$this->titleTemplate = $titleTemplate;
}
}

/**
* The Twig variant of the whole page templates.
*/
class TwigPageTemplate extends BasePageTemplate
{
public function getTemplateString(): string
{
$renderedTitle = $this->titleTemplate->getTemplateString();

return <<<HTML
<div class="page">
$renderedTitle
<article class="content">{{ content }}</article>
</div>
HTML;
}
}

/**
* The PHPTemplate variant of the whole page templates.
*/
class PHPTemplatePageTemplate extends BasePageTemplate
{
public function getTemplateString(): string
{
$renderedTitle = $this->titleTemplate->getTemplateString();

return <<<HTML
<div class="page">
$renderedTitle
<article class="content"><?= \$content; ?></article>
</div>
HTML;
}
}

/**
* The renderer is responsible for converting a template string into the actual
* HTML code. Each renderer behaves differently and expects its own type of
* template strings passed to it. Baking templates with the factory let you pass
* proper types of templates to proper renders.
*/
interface TemplateRenderer
{
public function render(string $templateString, array $arguments = []): string;
}

/**
* The renderer for Twig templates.
*/
class TwigRenderer implements TemplateRenderer
{
public function render(string $templateString, array $arguments = []): string
{
return \Twig::render($templateString, $arguments);
}
}

/**
* The renderer for PHPTemplate templates. Note that this implementation is very
* basic, if not crude. Using the `eval` function has many security
* implications, so use it with caution in real projects.
*/
class PHPTemplateRenderer implements TemplateRenderer
{
public function render(string $templateString, array $arguments = []): string
{
extract($arguments);

ob_start();
eval(' ?>' . $templateString . '<?php ');
$result = ob_get_contents();
ob_end_clean();

return $result;
}
}

/**
* The client code. Note that it accepts the Abstract Factory class as the
* parameter, which allows the client to work with any concrete factory type.
*/
class Page
{

public $title;

public $content;

public function __construct($title, $content)
{
$this->title = $title;
$this->content = $content;
}

// Here's how would you use the template further in real life. Note that the
// page class does not depend on any concrete template classes.
public function render(TemplateFactory $factory): string
{
$pageTemplate = $factory->createPageTemplate();

$renderer = $factory->getRenderer();

return $renderer->render($pageTemplate->getTemplateString(), [
'title' => $this->title,
'content' => $this->content
]);
}
}

/**
* Now, in other parts of the app, the client code can accept factory objects of
* any type.
*/
$page = new Page('Sample page', 'This it the body.');

echo "Testing actual rendering with the PHPTemplate factory:\n";
echo $page->render(new PHPTemplateFactory());


// Uncomment the following if you have Twig installed.

// echo "Testing rendering with the Twig factory:\n"; echo $page->render(new
// TwigTemplateFactory());

Output.txt: 执行结果

1
2
3
4
5
Testing actual rendering with the PHPTemplate factory:
<div class="page">
<h1>Sample page</h1>
<article class="content">This it the body.</article>
</div>

在 Python 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 抽象工厂模式在 Python 代码中很常见。 许多框架和程序库会将它作为扩展和自定义其标准组件的一种方式。

识别方法: 我们可以通过方法来识别该模式——其会返回一个工厂对象。 接下来, 工厂将被用于创建特定的子组件。

概念示例

本例说明了抽象工厂设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

main.py: 概念示例

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
157
from __future__ import annotations
from abc import ABC, abstractmethod


class AbstractFactory(ABC):
"""
The Abstract Factory interface declares a set of methods that return
different abstract products. These products are called a family and are
related by a high-level theme or concept. Products of one family are usually
able to collaborate among themselves. A family of products may have several
variants, but the products of one variant are incompatible with products of
another.
"""
@abstractmethod
def create_product_a(self) -> AbstractProductA:
pass

@abstractmethod
def create_product_b(self) -> AbstractProductB:
pass


class ConcreteFactory1(AbstractFactory):
"""
Concrete Factories produce a family of products that belong to a single
variant. The factory guarantees that resulting products are compatible. Note
that signatures of the Concrete Factory's methods return an abstract
product, while inside the method a concrete product is instantiated.
"""

def create_product_a(self) -> AbstractProductA:
return ConcreteProductA1()

def create_product_b(self) -> AbstractProductB:
return ConcreteProductB1()


class ConcreteFactory2(AbstractFactory):
"""
Each Concrete Factory has a corresponding product variant.
"""

def create_product_a(self) -> AbstractProductA:
return ConcreteProductA2()

def create_product_b(self) -> AbstractProductB:
return ConcreteProductB2()


class AbstractProductA(ABC):
"""
Each distinct product of a product family should have a base interface. All
variants of the product must implement this interface.
"""

@abstractmethod
def useful_function_a(self) -> str:
pass


"""
Concrete Products are created by corresponding Concrete Factories.
"""


class ConcreteProductA1(AbstractProductA):
def useful_function_a(self) -> str:
return "The result of the product A1."


class ConcreteProductA2(AbstractProductA):
def useful_function_a(self) -> str:
return "The result of the product A2."


class AbstractProductB(ABC):
"""
Here's the the base interface of another product. All products can interact
with each other, but proper interaction is possible only between products of
the same concrete variant.
"""
@abstractmethod
def useful_function_b(self) -> None:
"""
Product B is able to do its own thing...
"""
pass

@abstractmethod
def another_useful_function_b(self, collaborator: AbstractProductA) -> None:
"""
...but it also can collaborate with the ProductA.

The Abstract Factory makes sure that all products it creates are of the
same variant and thus, compatible.
"""
pass


"""
Concrete Products are created by corresponding Concrete Factories.
"""


class ConcreteProductB1(AbstractProductB):
def useful_function_b(self) -> str:
return "The result of the product B1."

"""
The variant, Product B1, is only able to work correctly with the variant,
Product A1. Nevertheless, it accepts any instance of AbstractProductA as an
argument.
"""

def another_useful_function_b(self, collaborator: AbstractProductA) -> str:
result = collaborator.useful_function_a()
return f"The result of the B1 collaborating with the ({result})"


class ConcreteProductB2(AbstractProductB):
def useful_function_b(self) -> str:
return "The result of the product B2."

def another_useful_function_b(self, collaborator: AbstractProductA):
"""
The variant, Product B2, is only able to work correctly with the
variant, Product A2. Nevertheless, it accepts any instance of
AbstractProductA as an argument.
"""
result = collaborator.useful_function_a()
return f"The result of the B2 collaborating with the ({result})"


def client_code(factory: AbstractFactory) -> None:
"""
The client code works with factories and products only through abstract
types: AbstractFactory and AbstractProduct. This lets you pass any factory
or product subclass to the client code without breaking it.
"""
product_a = factory.create_product_a()
product_b = factory.create_product_b()

print(f"{product_b.useful_function_b()}")
print(f"{product_b.another_useful_function_b(product_a)}", end="")


if __name__ == "__main__":
"""
The client code can work with any concrete factory class.
"""
print("Client: Testing client code with the first factory type:")
client_code(ConcreteFactory1())

print("\n")

print("Client: Testing the same client code with the second factory type:")
client_code(ConcreteFactory2())

Output.txt: 执行结果

1
2
3
4
5
6
7
Client: Testing client code with the first factory type:
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)

Client: Testing the same client code with the second factory type:
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)

在 Ruby 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 抽象工厂模式在 Ruby 代码中很常见。 许多框架和程序库会将它作为扩展和自定义其标准组件的一种方式。

识别方法: 我们可以通过方法来识别该模式——其会返回一个工厂对象。 接下来, 工厂将被用于创建特定的子组件。

概念示例

本例说明了抽象工厂设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各个元素会以何种方式相互关联?

main.rb: 概念示例

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
# The Abstract Factory interface declares a set of methods that return different
# abstract products. These products are called a family and are related by a
# high-level theme or concept. Products of one family are usually able to
# collaborate among themselves. A family of products may have several variants,
# but the products of one variant are incompatible with products of another.
class AbstractFactory
# @abstract
def create_product_a
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end

# @abstract
def create_product_b
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end

# Concrete Factories produce a family of products that belong to a single
# variant. The factory guarantees that resulting products are compatible. Note
# that signatures of the Concrete Factory's methods return an abstract product,
# while inside the method a concrete product is instantiated.
class ConcreteFactory1 < AbstractFactory
def create_product_a
ConcreteProductA1.new
end

def create_product_b
ConcreteProductB1.new
end
end

# Each Concrete Factory has a corresponding product variant.
class ConcreteFactory2 < AbstractFactory
def create_product_a
ConcreteProductA2.new
end

def create_product_b
ConcreteProductB2.new
end
end

# Each distinct product of a product family should have a base interface. All
# variants of the product must implement this interface.
class AbstractProductA
# @abstract
#
# @return [String]
def useful_function_a
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end

# Concrete Products are created by corresponding Concrete Factories.
class ConcreteProductA1 < AbstractProductA
def useful_function_a
'The result of the product A1.'
end
end

class ConcreteProductA2 < AbstractProductA
def useful_function_a
'The result of the product A2.'
end
end

# Here's the the base interface of another product. All products can interact
# with each other, but proper interaction is possible only between products of
# the same concrete variant.
class AbstractProductB
# Product B is able to do its own thing...
def useful_function_b
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end

# ...but it also can collaborate with the ProductA.
#
# The Abstract Factory makes sure that all products it creates are of the same
# variant and thus, compatible.
def another_useful_function_b(_collaborator)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end

# Concrete Products are created by corresponding Concrete Factories.
class ConcreteProductB1 < AbstractProductB
# @return [String]
def useful_function_b
'The result of the product B1.'
end

# The variant, Product B1, is only able to work correctly with the variant,
# Product A1. Nevertheless, it accepts any instance of AbstractProductA as an
# argument.
def another_useful_function_b(collaborator)
result = collaborator.useful_function_a
"The result of the B1 collaborating with the (#{result})"
end
end

class ConcreteProductB2 < AbstractProductB
# @return [String]
def useful_function_b
'The result of the product B2.'
end

# The variant, Product B2, is only able to work correctly with the variant,
# Product A2. Nevertheless, it accepts any instance of AbstractProductA as an
# argument.
def another_useful_function_b(collaborator)
result = collaborator.useful_function_a
"The result of the B2 collaborating with the (#{result})"
end
end

# The client code works with factories and products only through abstract types:
# AbstractFactory and AbstractProduct. This lets you pass any factory or product
# subclass to the client code without breaking it.
def client_code(factory)
product_a = factory.create_product_a
product_b = factory.create_product_b

puts product_b.useful_function_b.to_s
puts product_b.another_useful_function_b(product_a).to_s
end

# The client code can work with any concrete factory class.
puts 'Client: Testing client code with the first factory type:'
client_code(ConcreteFactory1.new)

puts "\n"

puts 'Client: Testing the same client code with the second factory type:'
client_code(ConcreteFactory2.new)

output.txt: 执行结果

1
2
3
4
5
6
7
Client: Testing client code with the first factory type:
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)

Client: Testing the same client code with the second factory type:
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)

在 Swift 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 抽象工厂模式在 Swift 代码中很常见。 许多框架和程序库会将它作为扩展和自定义其标准组件的一种方式。

识别方法: 我们可以通过方法来识别该模式——其会返回一个工厂对象。 接下来, 工厂将被用于创建特定的子组件。

概念示例

本例说明了抽象工厂设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各元素会以何种方式相互关联?

了解该模式的结构后, 你可以更容易地理解下面的基于实际情况的 Swift 应用案例。

Example.swift: 概念示例

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
import XCTest

/// The Abstract Factory protocol declares a set of methods that return
/// different abstract products. These products are called a family and are
/// related by a high-level theme or concept. Products of one family are usually
/// able to collaborate among themselves. A family of products may have several
/// variants, but the products of one variant are incompatible with products of
/// another.
protocol AbstractFactory {

func createProductA() -> AbstractProductA
func createProductB() -> AbstractProductB
}

/// Concrete Factories produce a family of products that belong to a single
/// variant. The factory guarantees that resulting products are compatible. Note
/// that signatures of the Concrete Factory's methods return an abstract
/// product, while inside the method a concrete product is instantiated.
class ConcreteFactory1: AbstractFactory {

func createProductA() -> AbstractProductA {
return ConcreteProductA1()
}

func createProductB() -> AbstractProductB {
return ConcreteProductB1()
}
}

/// Each Concrete Factory has a corresponding product variant.
class ConcreteFactory2: AbstractFactory {

func createProductA() -> AbstractProductA {
return ConcreteProductA2()
}

func createProductB() -> AbstractProductB {
return ConcreteProductB2()
}
}

/// Each distinct product of a product family should have a base protocol. All
/// variants of the product must implement this protocol.
protocol AbstractProductA {

func usefulFunctionA() -> String
}

/// Concrete Products are created by corresponding Concrete Factories.
class ConcreteProductA1: AbstractProductA {

func usefulFunctionA() -> String {
return "The result of the product A1."
}
}

class ConcreteProductA2: AbstractProductA {

func usefulFunctionA() -> String {
return "The result of the product A2."
}
}

/// The base protocol of another product. All products can interact with each
/// other, but proper interaction is possible only between products of the same
/// concrete variant.
protocol AbstractProductB {

/// Product B is able to do its own thing...
func usefulFunctionB() -> String

/// ...but it also can collaborate with the ProductA.
///
/// The Abstract Factory makes sure that all products it creates are of the
/// same variant and thus, compatible.
func anotherUsefulFunctionB(collaborator: AbstractProductA) -> String
}

/// Concrete Products are created by corresponding Concrete Factories.
class ConcreteProductB1: AbstractProductB {

func usefulFunctionB() -> String {
return "The result of the product B1."
}

/// This variant, Product B1, is only able to work correctly with the
/// variant, Product A1. Nevertheless, it accepts any instance of
/// AbstractProductA as an argument.
func anotherUsefulFunctionB(collaborator: AbstractProductA) -> String {
let result = collaborator.usefulFunctionA()
return "The result of the B1 collaborating with the (\(result))"
}
}

class ConcreteProductB2: AbstractProductB {

func usefulFunctionB() -> String {
return "The result of the product B2."
}

/// This variant, Product B2, is only able to work correctly with the
/// variant, Product A2. Nevertheless, it accepts any instance of
/// AbstractProductA as an argument.
func anotherUsefulFunctionB(collaborator: AbstractProductA) -> String {
let result = collaborator.usefulFunctionA()
return "The result of the B2 collaborating with the (\(result))"
}
}

/// The client code works with factories and products only through abstract
/// types: AbstractFactory and AbstractProduct. This lets you pass any factory
/// or product subclass to the client code without breaking it.
class Client {
// ...
static func someClientCode(factory: AbstractFactory) {
let productA = factory.createProductA()
let productB = factory.createProductB()

print(productB.usefulFunctionB())
print(productB.anotherUsefulFunctionB(collaborator: productA))
}
// ...
}

/// Let's see how it all works together.
class AbstractFactoryConceptual: XCTestCase {

func testAbstractFactoryConceptual() {

/// The client code can work with any concrete factory class.

print("Client: Testing client code with the first factory type:")
Client.someClientCode(factory: ConcreteFactory1())

print("Client: Testing the same client code with the second factory type:")
Client.someClientCode(factory: ConcreteFactory2())
}
}

Output.txt: 执行结果

1
2
3
4
5
6
Client: Testing client code with the first factory type:
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)
Client: Testing the same client code with the second factory type:
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)

真实世界示例

Example.swift: 真实世界示例

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
import Foundation
import UIKit
import XCTest

enum AuthType {
case login
case signUp
}


protocol AuthViewFactory {

static func authView(for type: AuthType) -> AuthView
static func authController(for type: AuthType) -> AuthViewController
}

class StudentAuthViewFactory: AuthViewFactory {

static func authView(for type: AuthType) -> AuthView {
print("Student View has been created")
switch type {
case .login: return StudentLoginView()
case .signUp: return StudentSignUpView()
}
}

static func authController(for type: AuthType) -> AuthViewController {
let controller = StudentAuthViewController(contentView: authView(for: type))
print("Student View Controller has been created")
return controller
}
}

class TeacherAuthViewFactory: AuthViewFactory {

static func authView(for type: AuthType) -> AuthView {
print("Teacher View has been created")
switch type {
case .login: return TeacherLoginView()
case .signUp: return TeacherSignUpView()
}
}

static func authController(for type: AuthType) -> AuthViewController {
let controller = TeacherAuthViewController(contentView: authView(for: type))
print("Teacher View Controller has been created")
return controller
}
}



protocol AuthView {

typealias AuthAction = (AuthType) -> ()

var contentView: UIView { get }
var authHandler: AuthAction? { get set }

var description: String { get }
}

class StudentSignUpView: UIView, AuthView {

private class StudentSignUpContentView: UIView {

/// This view contains a number of features available only during a
/// STUDENT authorization.
}

var contentView: UIView = StudentSignUpContentView()

/// The handler will be connected for actions of buttons of this view.
var authHandler: AuthView.AuthAction?

override var description: String {
return "Student-SignUp-View"
}
}

class StudentLoginView: UIView, AuthView {

private let emailField = UITextField()
private let passwordField = UITextField()
private let signUpButton = UIButton()

var contentView: UIView {
return self
}

/// The handler will be connected for actions of buttons of this view.
var authHandler: AuthView.AuthAction?

override var description: String {
return "Student-Login-View"
}
}



class TeacherSignUpView: UIView, AuthView {

class TeacherSignUpContentView: UIView {

/// This view contains a number of features available only during a
/// TEACHER authorization.
}

var contentView: UIView = TeacherSignUpContentView()

/// The handler will be connected for actions of buttons of this view.
var authHandler: AuthView.AuthAction?

override var description: String {
return "Teacher-SignUp-View"
}
}

class TeacherLoginView: UIView, AuthView {

private let emailField = UITextField()
private let passwordField = UITextField()
private let loginButton = UIButton()
private let forgotPasswordButton = UIButton()

var contentView: UIView {
return self
}

/// The handler will be connected for actions of buttons of this view.
var authHandler: AuthView.AuthAction?

override var description: String {
return "Teacher-Login-View"
}
}



class AuthViewController: UIViewController {

fileprivate var contentView: AuthView

init(contentView: AuthView) {
self.contentView = contentView
super.init(nibName: nil, bundle: nil)
}

required convenience init?(coder aDecoder: NSCoder) {
return nil
}
}

class StudentAuthViewController: AuthViewController {

/// Student-oriented features
}

class TeacherAuthViewController: AuthViewController {

/// Teacher-oriented features
}


private class ClientCode {

private var currentController: AuthViewController?

private lazy var navigationController: UINavigationController = {
guard let vc = currentController else { return UINavigationController() }
return UINavigationController(rootViewController: vc)
}()

private let factoryType: AuthViewFactory.Type

init(factoryType: AuthViewFactory.Type) {
self.factoryType = factoryType
}

/// MARK: - Presentation

func presentLogin() {
let controller = factoryType.authController(for: .login)
navigationController.pushViewController(controller, animated: true)
}

func presentSignUp() {
let controller = factoryType.authController(for: .signUp)
navigationController.pushViewController(controller, animated: true)
}

/// Other methods...
}


class AbstractFactoryRealWorld: XCTestCase {

func testFactoryMethodRealWorld() {

#if teacherMode
let clientCode = ClientCode(factoryType: TeacherAuthViewFactory.self)
#else
let clientCode = ClientCode(factoryType: StudentAuthViewFactory.self)
#endif

/// Present LogIn flow
clientCode.presentLogin()
print("Login screen has been presented")

/// Present SignUp flow
clientCode.presentSignUp()
print("Sign up screen has been presented")
}
}

Output.txt: 执行结果

1
2
3
4
5
6
Teacher View has been created
Teacher View Controller has been created
Login screen has been presented
Teacher View has been created
Teacher View Controller has been created
Sign up screen has been presented

在 TypeScript 中使用模式

复杂度: ★★☆

流行度: ★★★

使用示例: 抽象工厂模式在 TypeScript 代码中很常见。 许多框架和程序库会将它作为扩展和自定义其标准组件的一种方式。

识别方法: 我们可以通过方法来识别该模式——其会返回一个工厂对象。 接下来, 工厂将被用于创建特定的子组件。

概念示例

本例说明了抽象工厂设计模式的结构并重点回答了下面的问题:

  • 它由哪些类组成?
  • 这些类扮演了哪些角色?
  • 模式中的各元素会以何种方式相互关联?

index.ts: 概念示例

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
/**
* The Abstract Factory interface declares a set of methods that return
* different abstract products. These products are called a family and are
* related by a high-level theme or concept. Products of one family are usually
* able to collaborate among themselves. A family of products may have several
* variants, but the products of one variant are incompatible with products of
* another.
*/
interface AbstractFactory {
createProductA(): AbstractProductA;

createProductB(): AbstractProductB;
}

/**
* Concrete Factories produce a family of products that belong to a single
* variant. The factory guarantees that resulting products are compatible. Note
* that signatures of the Concrete Factory's methods return an abstract product,
* while inside the method a concrete product is instantiated.
*/
class ConcreteFactory1 implements AbstractFactory {
public createProductA(): AbstractProductA {
return new ConcreteProductA1();
}

public createProductB(): AbstractProductB {
return new ConcreteProductB1();
}
}

/**
* Each Concrete Factory has a corresponding product variant.
*/
class ConcreteFactory2 implements AbstractFactory {
public createProductA(): AbstractProductA {
return new ConcreteProductA2();
}

public createProductB(): AbstractProductB {
return new ConcreteProductB2();
}
}

/**
* Each distinct product of a product family should have a base interface. All
* variants of the product must implement this interface.
*/
interface AbstractProductA {
usefulFunctionA(): string;
}

/**
* These Concrete Products are created by corresponding Concrete Factories.
*/
class ConcreteProductA1 implements AbstractProductA {
public usefulFunctionA(): string {
return 'The result of the product A1.';
}
}

class ConcreteProductA2 implements AbstractProductA {
public usefulFunctionA(): string {
return 'The result of the product A2.';
}
}

/**
* Here's the the base interface of another product. All products can interact
* with each other, but proper interaction is possible only between products of
* the same concrete variant.
*/
interface AbstractProductB {
/**
* Product B is able to do its own thing...
*/
usefulFunctionB(): string;

/**
* ...but it also can collaborate with the ProductA.
*
* The Abstract Factory makes sure that all products it creates are of the
* same variant and thus, compatible.
*/
anotherUsefulFunctionB(collaborator: AbstractProductA): string;
}

/**
* These Concrete Products are created by corresponding Concrete Factories.
*/
class ConcreteProductB1 implements AbstractProductB {

public usefulFunctionB(): string {
return 'The result of the product B1.';
}

/**
* The variant, Product B1, is only able to work correctly with the variant,
* Product A1. Nevertheless, it accepts any instance of AbstractProductA as
* an argument.
*/
public anotherUsefulFunctionB(collaborator: AbstractProductA): string {
const result = collaborator.usefulFunctionA();
return `The result of the B1 collaborating with the (${result})`;
}
}

class ConcreteProductB2 implements AbstractProductB {

public usefulFunctionB(): string {
return 'The result of the product B2.';
}

/**
* The variant, Product B2, is only able to work correctly with the variant,
* Product A2. Nevertheless, it accepts any instance of AbstractProductA as
* an argument.
*/
public anotherUsefulFunctionB(collaborator: AbstractProductA): string {
const result = collaborator.usefulFunctionA();
return `The result of the B2 collaborating with the (${result})`;
}
}

/**
* The client code works with factories and products only through abstract
* types: AbstractFactory and AbstractProduct. This lets you pass any factory or
* product subclass to the client code without breaking it.
*/
function clientCode(factory: AbstractFactory) {
const productA = factory.createProductA();
const productB = factory.createProductB();

console.log(productB.usefulFunctionB());
console.log(productB.anotherUsefulFunctionB(productA));
}

/**
* The client code can work with any concrete factory class.
*/
console.log('Client: Testing client code with the first factory type...');
clientCode(new ConcreteFactory1());

console.log('');

console.log('Client: Testing the same client code with the second factory type...');
clientCode(new ConcreteFactory2());

Output.txt: 执行结果

1
2
3
4
5
6
7
Client: Testing client code with the first factory type...
The result of the product B1.
The result of the B1 collaborating with the (The result of the product A1.)

Client: Testing the same client code with the second factory type...
The result of the product B2.
The result of the B2 collaborating with the (The result of the product A2.)

概念示例

让我们假设一下, 如果你想要购买一组运动装备, 比如一双鞋与一件衬衫这样由两种不同产品组合而成的套装。 相信你会想去购买同一品牌的商品, 这样商品之间能够互相搭配起来。

如果我们把这样的行为转换成代码的话, 帮助我们创建此类产品组的工具就是抽象工厂, 便于产品之间能够相互匹配。

iSportsFactory.go: 抽象工厂接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import "fmt"

type iSportsFactory interface {
makeShoe() iShoe
makeShirt() iShirt
}

func getSportsFactory(brand string) (iSportsFactory, error) {
if brand == "adidas" {
return &adidas{}, nil
}

if brand == "nike" {
return &nike{}, nil
}

return nil, fmt.Errorf("Wrong brand type passed")
}

adidas.go: 具体工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

type adidas struct {
}

func (a *adidas) makeShoe() iShoe {
return &adidasShoe{
shoe: shoe{
logo: "adidas",
size: 14,
},
}
}

func (a *adidas) makeShirt() iShirt {
return &adidasShirt{
shirt: shirt{
logo: "adidas",
size: 14,
},
}
}

nike.go: 具体工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

type nike struct {
}

func (n *nike) makeShoe() iShoe {
return &nikeShoe{
shoe: shoe{
logo: "nike",
size: 14,
},
}
}

func (n *nike) makeShirt() iShirt {
return &nikeShirt{
shirt: shirt{
logo: "nike",
size: 14,
},
}
}

iShoe.go: 抽象产品

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
package main

type iShoe interface {
setLogo(logo string)
setSize(size int)
getLogo() string
getSize() int
}

type shoe struct {
logo string
size int
}

func (s *shoe) setLogo(logo string) {
s.logo = logo
}

func (s *shoe) getLogo() string {
return s.logo
}

func (s *shoe) setSize(size int) {
s.size = size
}

func (s *shoe) getSize() int {
return s.size
}

adidasShoe.go: 具体产品

1
2
3
4
5
package main

type adidasShoe struct {
shoe
}

nikeShoe.go: 具体产品

1
2
3
4
5
package main

type nikeShoe struct {
shoe
}

iShirt.go: 抽象产品

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
package main

type iShirt interface {
setLogo(logo string)
setSize(size int)
getLogo() string
getSize() int
}

type shirt struct {
logo string
size int
}

func (s *shirt) setLogo(logo string) {
s.logo = logo
}

func (s *shirt) getLogo() string {
return s.logo
}

func (s *shirt) setSize(size int) {
s.size = size
}

func (s *shirt) getSize() int {
return s.size
}

adidasShirt.go: 具体产品

1
2
3
4
5
package main

type adidasShirt struct {
shirt
}

nikeShirt.go: 具体产品

1
2
3
4
5
package main

type nikeShirt struct {
shirt
}

main.go: 客户端代码

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
package main

import "fmt"

func main() {
adidasFactory, _ := getSportsFactory("adidas")
nikeFactory, _ := getSportsFactory("nike")

nikeShoe := nikeFactory.makeShoe()
nikeShirt := nikeFactory.makeShirt()

adidasShoe := adidasFactory.makeShoe()
adidasShirt := adidasFactory.makeShirt()

printShoeDetails(nikeShoe)
printShirtDetails(nikeShirt)

printShoeDetails(adidasShoe)
printShirtDetails(adidasShirt)
}

func printShoeDetails(s iShoe) {
fmt.Printf("Logo: %s", s.getLogo())
fmt.Println()
fmt.Printf("Size: %d", s.getSize())
fmt.Println()
}

func printShirtDetails(s iShirt) {
fmt.Printf("Logo: %s", s.getLogo())
fmt.Println()
fmt.Printf("Size: %d", s.getSize())
fmt.Println()
}

output.txt: 执行结果

1
2
3
4
5
6
7
8
Logo: nike
Size: 14
Logo: nike
Size: 14
Logo: adidas
Size: 14
Logo: adidas
Size: 14

根据: Golang By Example