意图

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

问题

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

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

系列产品及其不同变体

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

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

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

解决方案

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

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

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

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

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

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

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

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

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

结构

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

实现方式

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

代码演示

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();
}
}
}

执行结果:

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.)

参考原文:抽象工厂设计模式