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

创建型模式-工厂方法模式

亦称:虚拟构造函数、Virtual Constructor、Factory Method

意图

工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。

问题

假设你正在开发一款物流管理应用。 最初版本只能处理卡车运输, 因此大部分代码都在位于名为 卡车的类中。

一段时间后, 这款应用变得极受欢迎。 你每天都能收到十几次来自海运公司的请求, 希望应用能够支持海上物流功能。

如果代码其余部分与现有类已经存在耦合关系, 那么向程序中添加新类其实并没有那么容易。

这可是个好消息。 但是代码问题该如何处理呢? 目前, 大部分代码都与 卡车类相关。 在程序中添加 轮船类需要修改全部代码。 更糟糕的是, 如果你以后需要在程序中支持另外一种运输方式, 很可能需要再次对这些代码进行大幅修改。

最后, 你将不得不编写繁复的代码, 根据不同的运输对象类, 在应用中进行不同的处理。

解决方案

工厂方法模式建议使用特殊的工厂方法代替对于对象构造函数的直接调用 (即使用 new运算符)。 不用担心, 对象仍将通过 new运算符创建, 只是该运算符改在工厂方法中调用罢了。 工厂方法返回的对象通常被称作 “产品”。

子类可以修改工厂方法返回的对象类型。

乍看之下, 这种更改可能毫无意义: 我们只是改变了程序中调用构造函数的位置而已。 但是, 仔细想一下, 现在你可以在子类中重写工厂方法, 从而改变其创建产品的类型。

但有一点需要注意:仅当这些产品具有共同的基类或者接口时, 子类才能返回不同类型的产品, 同时基类中的工厂方法还应将其返回类型声明为这一共有接口。

所有产品都必须使用同一接口。

举例来说, 卡车Truck和 轮船Ship类都必须实现 运输Transport接口, 该接口声明了一个名为 deliver交付的方法。 每个类都将以不同的方式实现该方法: 卡车走陆路交付货物, 轮船走海路交付货物。 陆路运输Road­Logistics类中的工厂方法返回卡车对象, 而 海路运输Sea­Logistics类则返回轮船对象。

只要产品类实现一个共同的接口, 你就可以将其对象传递给客户代码, 而无需提供额外数据。

调用工厂方法的代码 (通常被称为客户端代码) 无需了解不同子类返回实际对象之间的差别。 客户端将所有产品视为抽象的 运输 。 客户端知道所有运输对象都提供 交付方法, 但是并不关心其具体实现方式。

工厂方法模式结构

  1. 产品 (Product) 将会对接口进行声明。 对于所有由创建者及其子类构建的对象, 这些接口都是通用的。

  2. 具体产品 (Concrete Products) 是产品接口的不同实现。

  3. 创建者 (Creator) 类声明返回产品对象的工厂方法。 该方法的返回对象类型必须与产品接口相匹配。

    你可以将工厂方法声明为抽象方法, 强制要求每个子类以不同方式实现该方法。 或者, 你也可以在基础工厂方法中返回默认产品类型。

    注意, 尽管它的名字是创建者, 但他最主要的职责并不是创建产品。 一般来说, 创建者类包含一些与产品相关的核心业务逻辑。 工厂方法将这些逻辑处理从具体产品类中分离出来。 打个比方, 大型软件开发公司拥有程序员培训部门。 但是, 这些公司的主要工作还是编写代码, 而非生产程序员。

  4. 具体创建者 (Concrete Creators) 将会重写基础工厂方法, 使其返回不同类型的产品。

    注意, 并不一定每次调用工厂方法都会创建新的实例。 工厂方法也可以返回缓存、 对象池或其他来源的已有对象。

伪代码

以下示例演示了如何使用工厂方法开发跨平台 UI (用户界面) 组件, 并同时避免客户代码与具体 UI 类之间的耦合。

跨平台对话框示例。

基础对话框类使用不同的 UI 组件渲染窗口。 在不同的操作系统下, 这些组件外观或许略有不同, 但其功能保持一致。 Windows 系统中的按钮在 Linux 系统中仍然是按钮。

如果使用工厂方法, 就不需要为每种操作系统重写对话框逻辑。 如果我们声明了一个在基本对话框类中生成按钮的工厂方法, 那么我们就可以创建一个对话框子类, 并使其通过工厂方法返回 Windows 样式按钮。 子类将继承对话框基础类的大部分代码, 同时在屏幕上根据 Windows 样式渲染按钮。

如需该模式正常工作, 基础对话框类必须使用抽象按钮 (例如基类或接口), 以便将其扩展为具体按钮。 这样一来, 无论对话框中使用何种类型的按钮, 其代码都可以正常工作。

你可以使用此方法开发其他 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
// 创建者类声明的工厂方法必须返回一个产品类的对象。创建者的子类通常会提供
// 该方法的实现。
class Dialog is
// 创建者还可提供一些工厂方法的默认实现。
abstract method createButton():Button

// 请注意,创建者的主要职责并非是创建产品。其中通常会包含一些核心业务
// 逻辑,这些逻辑依赖于由工厂方法返回的产品对象。子类可通过重写工厂方
// 法并使其返回不同类型的产品来间接修改业务逻辑。
method render() is
// 调用工厂方法创建一个产品对象。
Button okButton = createButton()
// 现在使用产品。
okButton.onClick(closeDialog)
okButton.render()


// 具体创建者将重写工厂方法以改变其所返回的产品类型。
class WindowsDialog extends Dialog is
method createButton():Button is
return new WindowsButton()

class WebDialog extends Dialog is
method createButton():Button is
return new HTMLButton()


// 产品接口中将声明所有具体产品都必须实现的操作。
interface Button is
method render()
method onClick(f)

// 具体产品需提供产品接口的各种实现。
class WindowsButton implements Button is
method render(a, b) is
// 根据 Windows 样式渲染按钮。
method onClick(f) is
// 绑定本地操作系统点击事件。

class HTMLButton implements Button is
method render(a, b) is
// 返回一个按钮的 HTML 表述。
method onClick(f) is
// 绑定网络浏览器的点击事件。


class Application is
field dialog: Dialog

// 程序根据当前配置或环境设定选择创建者的类型。
method initialize() is
config = readApplicationConfigFile()

if (config.OS == "Windows") then
dialog = new WindowsDialog()
else if (config.OS == "Web") then
dialog = new WebDialog()
else
throw new Exception("错误!未知的操作系统。")

// 当前客户端代码会与具体创建者的实例进行交互,但是必须通过其基本接口
// 进行。只要客户端通过基本接口与创建者进行交互,你就可将任何创建者子
// 类传递给客户端。
method main() is
this.initialize()
dialog.render()

工厂方法模式适合应用场景

当你在编写代码的过程中,如果无法预知对象确切类别及其依赖关系时,可使用工厂方法。

工厂方法将创建产品的代码与实际使用产品的代码分离, 从而能在不影响其他代码的情况下扩展产品创建部分代码。

例如, 如果需要向应用中添加一种新产品, 你只需要开发新的创建者子类, 然后重写其工厂方法即可。

如果你希望用户能扩展你软件库或框架的内部组件,可使用工厂方法。

继承可能是扩展软件库或框架默认行为的最简单方法。 但是当你使用子类替代标准组件时, 框架如何辨识出该子类?

解决方案是将各框架中构造组件的代码集中到单个工厂方法中, 并在继承该组件之外允许任何人对该方法进行重写。

让我们看看具体是如何实现的。 假设你使用开源 UI 框架编写自己的应用。 你希望在应用中使用圆形按钮, 但是原框架仅支持矩形按钮。 你可以使用 圆形按钮Round­Button子类来继承标准的 按钮Button类。 但是, 你需要告诉 UI框架UIFramework类使用新的子类按钮代替默认按钮。

为了实现这个功能, 你可以根据基础框架类开发子类 圆形按钮 UIUIWith­Round­Buttons , 并且重写其 create­Button创建按钮方法。 基类中的该方法返回 按钮对象, 而你开发的子类返回 圆形按钮对象。 现在, 你就可以使用 圆形按钮 UI类代替 UI框架类。 就是这么简单!

如果你希望复用现有对象来节省系统资源,而不是每次都重新创建对象,可使用工厂方法。

在处理大型资源密集型对象 (比如数据库连接、 文件系统和网络资源) 时, 你会经常碰到这种资源需求。

让我们思考复用现有对象的方法:

  1. 首先, 你需要创建存储空间来存放所有已经创建的对象。
  2. 当他人请求一个对象时, 程序将在对象池中搜索可用对象。
  3. …然后将其返回给客户端代码。
  4. 如果没有可用对象, 程序则创建一个新对象 (并将其添加到对象池中)。

这些代码可不少! 而且它们必须位于同一处, 这样才能确保重复代码不会污染程序。

可能最显而易见, 也是最方便的方式, 就是将这些代码放置在我们试图重用的对象类的构造函数中。 但是从定义上来讲, 构造函数始终返回的是新对象, 其无法返回现有实例。

因此, 你需要有一个既能够创建新对象, 又可以重用现有对象的普通方法。 这听上去和工厂方法非常相像。

实现方式

  1. 让所有产品都遵循同一接口。 该接口必须声明对所有产品都有意义的方法。

  2. 在创建类中添加一个空的工厂方法。 该方法的返回类型必须遵循通用的产品接口。

  3. 在创建者代码中找到对于产品构造函数的所有引用。 将它们依次替换为对于工厂方法的调用, 同时将创建产品的代码移入工厂方法。 你可能需要在工厂方法中添加临时参数来控制返回的产品类型。

    工厂方法的代码看上去可能非常糟糕。 其中可能会有复杂的 switch分支运算符, 用于选择各种需要实例化的产品类。 但是不要担心, 我们很快就会修复这个问题。

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

namespace RefactoringGuru.DesignPatterns.FactoryMethod.Conceptual
{
// The Creator class declares the factory method that is supposed to return
// an object of a Product class. The Creator's subclasses usually provide
// the implementation of this method.
abstract class Creator
{
// Note that the Creator may also provide some default implementation of
// the factory method.
public abstract IProduct FactoryMethod();

// Also note that, despite its name, the Creator's primary
// responsibility is not creating products. Usually, it contains some
// core business logic that relies on Product objects, returned by the
// factory method. Subclasses can indirectly change that business logic
// by overriding the factory method and returning a different type of
// product from it.
public string SomeOperation()
{
// Call the factory method to create a Product object.
var product = FactoryMethod();
// Now, use the product.
var result = "Creator: The same creator's code has just worked with "
+ product.Operation();

return result;
}
}

// Concrete Creators override the factory method in order to change the
// resulting product's type.
class ConcreteCreator1 : Creator
{
// Note that the signature of the method still uses the abstract product
// type, even though the concrete product is actually returned from the
// method. This way the Creator can stay independent of concrete product
// classes.
public override IProduct FactoryMethod()
{
return new ConcreteProduct1();
}
}

class ConcreteCreator2 : Creator
{
public override IProduct FactoryMethod()
{
return new ConcreteProduct2();
}
}

// The Product interface declares the operations that all concrete products
// must implement.
public interface IProduct
{
string Operation();
}

// Concrete Products provide various implementations of the Product
// interface.
class ConcreteProduct1 : IProduct
{
public string Operation()
{
return "{Result of ConcreteProduct1}";
}
}

class ConcreteProduct2 : IProduct
{
public string Operation()
{
return "{Result of ConcreteProduct2}";
}
}

class Client
{
public void Main()
{
Console.WriteLine("App: Launched with the ConcreteCreator1.");
ClientCode(new ConcreteCreator1());

Console.WriteLine("");

Console.WriteLine("App: Launched with the ConcreteCreator2.");
ClientCode(new ConcreteCreator2());
}

// The client code works with an instance of a concrete creator, albeit
// through its base interface. As long as the client keeps working with
// the creator via the base interface, you can pass it any creator's
// subclass.
public void ClientCode(Creator creator)
{
// ...
Console.WriteLine("Client: I'm not aware of the creator's class," +
"but it still works.\n" + creator.SomeOperation());
// ...
}
}

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

Output.txt: 执行结果

1
2
3
4
5
6
7
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of ConcreteProduct2}

在 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
/**
* The Product interface declares the operations that all concrete products must
* implement.
*/

class Product {
public:
virtual ~Product() {}
virtual std::string Operation() const = 0;
};

/**
* Concrete Products provide various implementations of the Product interface.
*/
class ConcreteProduct1 : public Product {
public:
std::string Operation() const override {
return "{Result of the ConcreteProduct1}";
}
};
class ConcreteProduct2 : public Product {
public:
std::string Operation() const override {
return "{Result of the ConcreteProduct2}";
}
};

/**
* The Creator class declares the factory method that is supposed to return an
* object of a Product class. The Creator's subclasses usually provide the
* implementation of this method.
*/

class Creator {
/**
* Note that the Creator may also provide some default implementation of the
* factory method.
*/
public:
virtual ~Creator(){};
virtual Product* FactoryMethod() const = 0;
/**
* Also note that, despite its name, the Creator's primary responsibility is
* not creating products. Usually, it contains some core business logic that
* relies on Product objects, returned by the factory method. Subclasses can
* indirectly change that business logic by overriding the factory method and
* returning a different type of product from it.
*/

std::string SomeOperation() const {
// Call the factory method to create a Product object.
Product* product = this->FactoryMethod();
// Now, use the product.
std::string result = "Creator: The same creator's code has just worked with " + product->Operation();
delete product;
return result;
}
};

/**
* Concrete Creators override the factory method in order to change the
* resulting product's type.
*/
class ConcreteCreator1 : public Creator {
/**
* Note that the signature of the method still uses the abstract product type,
* even though the concrete product is actually returned from the method. This
* way the Creator can stay independent of concrete product classes.
*/
public:
Product* FactoryMethod() const override {
return new ConcreteProduct1();
}
};

class ConcreteCreator2 : public Creator {
public:
Product* FactoryMethod() const override {
return new ConcreteProduct2();
}
};

/**
* The client code works with an instance of a concrete creator, albeit through
* its base interface. As long as the client keeps working with the creator via
* the base interface, you can pass it any creator's subclass.
*/
void ClientCode(const Creator& creator) {
// ...
std::cout << "Client: I'm not aware of the creator's class, but it still works.\n"
<< creator.SomeOperation() << std::endl;
// ...
}

/**
* The Application picks a creator's type depending on the configuration or
* environment.
*/

int main() {
std::cout << "App: Launched with the ConcreteCreator1.\n";
Creator* creator = new ConcreteCreator1();
ClientCode(*creator);
std::cout << std::endl;
std::cout << "App: Launched with the ConcreteCreator2.\n";
Creator* creator2 = new ConcreteCreator2();
ClientCode(*creator2);

delete creator;
delete creator2;
return 0;
}

Output.txt: 执行结果

1
2
3
4
5
6
7
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}

在 Java 中使用模式

复杂度: ★☆☆

流行度: ★★★

使用示例: 工厂方法模式在 Java 代码中得到了广泛使用。 当你需要在代码中提供高层次的灵活性时, 该模式会非常实用。

核心 Java 程序库中有该模式的应用:

识别方法: 工厂方法可通过构建方法来识别, 它会创建具体类的对象, 但以抽象类型或接口的形式返回这些对象。

生成跨平台的 GUI 元素

在本例中, 按钮担任产品的角色, 对话框担任创建者的角色。

不同类型的对话框需要其各自类型的元素。 因此我们可为每个对话框类型创建子类并重写其工厂方法。

现在, 每种对话框类型都将对合适的按钮类进行初始化。 对话框基类使用其通用接口与对象进行交互, 因此代码更改后仍能正常工作。

buttons

buttons/Button.java: 通用产品接口

1
2
3
4
5
6
7
8
9
package refactoring_guru.factory_method.example.buttons;

/**
* Common interface for all buttons.
*/
public interface Button {
void render();
void onClick();
}

buttons/HtmlButton.java: 具体产品

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

/**
* HTML button implementation.
*/
public class HtmlButton implements Button {

public void render() {
System.out.println("<button>Test Button</button>");
onClick();
}

public void onClick() {
System.out.println("Click! Button says - 'Hello World!'");
}
}

buttons/WindowsButton.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
37
38
39
40
41
42
43
package refactoring_guru.factory_method.example.buttons;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
* Windows button implementation.
*/
public class WindowsButton implements Button {
JPanel panel = new JPanel();
JFrame frame = new JFrame();
JButton button;

public void render() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel label = new JLabel("Hello World!");
label.setOpaque(true);
label.setBackground(new Color(235, 233, 126));
label.setFont(new Font("Dialog", Font.BOLD, 44));
label.setHorizontalAlignment(SwingConstants.CENTER);
panel.setLayout(new FlowLayout(FlowLayout.CENTER));
frame.getContentPane().add(panel);
panel.add(label);
onClick();
panel.add(button);

frame.setSize(320, 200);
frame.setVisible(true);
onClick();
}

public void onClick() {
button = new JButton("Exit");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
frame.setVisible(false);
System.exit(0);
}
});
}
}

factory

factory/Dialog.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.factory_method.example.factory;

import refactoring_guru.factory_method.example.buttons.Button;

/**
* Base factory class. Note that "factory" is merely a role for the class. It
* should have some core business logic which needs different products to be
* created.
*/
public abstract class Dialog {

public void renderWindow() {
// ... other code ...

Button okButton = createButton();
okButton.render();
}

/**
* Subclasses will override this method in order to create specific button
* objects.
*/
public abstract Button createButton();
}

factory/HtmlDialog.java: 具体创建者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package refactoring_guru.factory_method.example.factory;

import refactoring_guru.factory_method.example.buttons.Button;
import refactoring_guru.factory_method.example.buttons.HtmlButton;

/**
* HTML Dialog will produce HTML buttons.
*/
public class HtmlDialog extends Dialog {

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

factory/WindowsDialog.java: 另一个具体创建者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package refactoring_guru.factory_method.example.factory;

import refactoring_guru.factory_method.example.buttons.Button;
import refactoring_guru.factory_method.example.buttons.WindowsButton;

/**
* Windows Dialog will produce Windows buttons.
*/
public class WindowsDialog extends Dialog {

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

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
37
38
package refactoring_guru.factory_method.example;

import refactoring_guru.factory_method.example.factory.Dialog;
import refactoring_guru.factory_method.example.factory.HtmlDialog;
import refactoring_guru.factory_method.example.factory.WindowsDialog;

/**
* Demo class. Everything comes together here.
*/
public class Demo {
private static Dialog dialog;

public static void main(String[] args) {
configure();
runBusinessLogic();
}

/**
* The concrete factory is usually chosen depending on configuration or
* environment options.
*/
static void configure() {
if (System.getProperty("os.name").equals("Windows 10")) {
dialog = new WindowsDialog();
} else {
dialog = new HtmlDialog();
}
}

/**
* All of the client code should work with factories and products through
* abstract interfaces. This way it does not care which factory it works
* with and what kind of product it returns.
*/
static void runBusinessLogic() {
dialog.renderWindow();
}
}

OutputDemo.txt: 执行结果 (Html­Dialog)

1
2
<button>Test Button</button>
Click! Button says - 'Hello World!'

OutputDemo.png: 执行结果 (Windows­Dialog)

在 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
<?php

namespace RefactoringGuru\FactoryMethod\Conceptual;

/**
* The Creator class declares the factory method that is supposed to return an
* object of a Product class. The Creator's subclasses usually provide the
* implementation of this method.
*/
abstract class Creator
{
/**
* Note that the Creator may also provide some default implementation of the
* factory method.
*/
abstract public function factoryMethod(): Product;

/**
* Also note that, despite its name, the Creator's primary responsibility is
* not creating products. Usually, it contains some core business logic that
* relies on Product objects, returned by the factory method. Subclasses can
* indirectly change that business logic by overriding the factory method
* and returning a different type of product from it.
*/
public function someOperation(): string
{
// Call the factory method to create a Product object.
$product = $this->factoryMethod();
// Now, use the product.
$result = "Creator: The same creator's code has just worked with " .
$product->operation();

return $result;
}
}

/**
* Concrete Creators override the factory method in order to change the
* resulting product's type.
*/
class ConcreteCreator1 extends Creator
{
/**
* Note that the signature of the method still uses the abstract product
* type, even though the concrete product is actually returned from the
* method. This way the Creator can stay independent of concrete product
* classes.
*/
public function factoryMethod(): Product
{
return new ConcreteProduct1();
}
}

class ConcreteCreator2 extends Creator
{
public function factoryMethod(): Product
{
return new ConcreteProduct2();
}
}

/**
* The Product interface declares the operations that all concrete products must
* implement.
*/
interface Product
{
public function operation(): string;
}

/**
* Concrete Products provide various implementations of the Product interface.
*/
class ConcreteProduct1 implements Product
{
public function operation(): string
{
return "{Result of the ConcreteProduct1}";
}
}

class ConcreteProduct2 implements Product
{
public function operation(): string
{
return "{Result of the ConcreteProduct2}";
}
}

/**
* The client code works with an instance of a concrete creator, albeit through
* its base interface. As long as the client keeps working with the creator via
* the base interface, you can pass it any creator's subclass.
*/
function clientCode(Creator $creator)
{
// ...
echo "Client: I'm not aware of the creator's class, but it still works.\n"
. $creator->someOperation();
// ...
}

/**
* The Application picks a creator's type depending on the configuration or
* environment.
*/
echo "App: Launched with the ConcreteCreator1.\n";
clientCode(new ConcreteCreator1());
echo "\n\n";

echo "App: Launched with the ConcreteCreator2.\n";
clientCode(new ConcreteCreator2());

Output.txt: 执行结果

1
2
3
4
5
6
7
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}

真实世界示例

在本例中, 工厂方法模式为创建社交网络连接器提供接口, 可用于进行登录网络、 发帖和潜在的其他行为, 而实现所有这些功能都无需客户端代码与特定社交网络的特定类相耦合。

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
<?php

namespace RefactoringGuru\FactoryMethod\RealWorld;

/**
* The Creator declares a factory method that can be used as a substitution for
* the direct constructor calls of products, for instance:
*
* - Before: $p = new FacebookConnector();
* - After: $p = $this->getSocialNetwork;
*
* This allows changing the type of the product being created by
* SocialNetworkPoster's subclasses.
*/
abstract class SocialNetworkPoster
{
/**
* The actual factory method. Note that it returns the abstract connector.
* This lets subclasses return any concrete connectors without breaking the
* superclass' contract.
*/
abstract public function getSocialNetwork(): SocialNetworkConnector;

/**
* When the factory method is used inside the Creator's business logic, the
* subclasses may alter the logic indirectly by returning different types of
* the connector from the factory method.
*/
public function post($content): void
{
// Call the factory method to create a Product object...
$network = $this->getSocialNetwork();
// ...then use it as you will.
$network->logIn();
$network->createPost($content);
$network->logout();
}
}

/**
* This Concrete Creator supports Facebook. Remember that this class also
* inherits the 'post' method from the parent class. Concrete Creators are the
* classes that the Client actually uses.
*/
class FacebookPoster extends SocialNetworkPoster
{
private $login, $password;

public function __construct(string $login, string $password)
{
$this->login = $login;
$this->password = $password;
}

public function getSocialNetwork(): SocialNetworkConnector
{
return new FacebookConnector($this->login, $this->password);
}
}

/**
* This Concrete Creator supports LinkedIn.
*/
class LinkedInPoster extends SocialNetworkPoster
{
private $email, $password;

public function __construct(string $email, string $password)
{
$this->email = $email;
$this->password = $password;
}

public function getSocialNetwork(): SocialNetworkConnector
{
return new LinkedInConnector($this->email, $this->password);
}
}

/**
* The Product interface declares behaviors of various types of products.
*/
interface SocialNetworkConnector
{
public function logIn(): void;

public function logOut(): void;

public function createPost($content): void;
}

/**
* This Concrete Product implements the Facebook API.
*/
class FacebookConnector implements SocialNetworkConnector
{
private $login, $password;

public function __construct(string $login, string $password)
{
$this->login = $login;
$this->password = $password;
}

public function logIn(): void
{
echo "Send HTTP API request to log in user $this->login with " .
"password $this->password\n";
}

public function logOut(): void
{
echo "Send HTTP API request to log out user $this->login\n";
}

public function createPost($content): void
{
echo "Send HTTP API requests to create a post in Facebook timeline.\n";
}
}

/**
* This Concrete Product implements the LinkedIn API.
*/
class LinkedInConnector implements SocialNetworkConnector
{
private $email, $password;

public function __construct(string $email, string $password)
{
$this->email = $email;
$this->password = $password;
}

public function logIn(): void
{
echo "Send HTTP API request to log in user $this->email with " .
"password $this->password\n";
}

public function logOut(): void
{
echo "Send HTTP API request to log out user $this->email\n";
}

public function createPost($content): void
{
echo "Send HTTP API requests to create a post in LinkedIn timeline.\n";
}
}

/**
* The client code can work with any subclass of SocialNetworkPoster since it
* doesn't depend on concrete classes.
*/
function clientCode(SocialNetworkPoster $creator)
{
// ...
$creator->post("Hello world!");
$creator->post("I had a large hamburger this morning!");
// ...
}

/**
* During the initialization phase, the app can decide which social network it
* wants to work with, create an object of the proper subclass, and pass it to
* the client code.
*/
echo "Testing ConcreteCreator1:\n";
clientCode(new FacebookPoster("john_smith", "******"));
echo "\n\n";

echo "Testing ConcreteCreator2:\n";
clientCode(new LinkedInPoster("john_smith@example.com", "******"));

Output.txt: 执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Testing ConcreteCreator1:
Send HTTP API request to log in user john_smith with password ******
Send HTTP API requests to create a post in Facebook timeline.
Send HTTP API request to log out user john_smith
Send HTTP API request to log in user john_smith with password ******
Send HTTP API requests to create a post in Facebook timeline.
Send HTTP API request to log out user john_smith


Testing ConcreteCreator2:
Send HTTP API request to log in user john_smith@example.com with password ******
Send HTTP API requests to create a post in LinkedIn timeline.
Send HTTP API request to log out user john_smith@example.com
Send HTTP API request to log in user john_smith@example.com with password ******
Send HTTP API requests to create a post in LinkedIn timeline.
Send HTTP API request to log out user john_smith@example.com

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


class Creator(ABC):
"""
The Creator class declares the factory method that is supposed to return an
object of a Product class. The Creator's subclasses usually provide the
implementation of this method.
"""

@abstractmethod
def factory_method(self):
"""
Note that the Creator may also provide some default implementation of
the factory method.
"""
pass

def some_operation(self) -> str:
"""
Also note that, despite its name, the Creator's primary responsibility
is not creating products. Usually, it contains some core business logic
that relies on Product objects, returned by the factory method.
Subclasses can indirectly change that business logic by overriding the
factory method and returning a different type of product from it.
"""

# Call the factory method to create a Product object.
product = self.factory_method()

# Now, use the product.
result = f"Creator: The same creator's code has just worked with {product.operation()}"

return result


"""
Concrete Creators override the factory method in order to change the resulting
product's type.
"""


class ConcreteCreator1(Creator):
"""
Note that the signature of the method still uses the abstract product type,
even though the concrete product is actually returned from the method. This
way the Creator can stay independent of concrete product classes.
"""

def factory_method(self) -> Product:
return ConcreteProduct1()


class ConcreteCreator2(Creator):
def factory_method(self) -> Product:
return ConcreteProduct2()


class Product(ABC):
"""
The Product interface declares the operations that all concrete products
must implement.
"""

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


"""
Concrete Products provide various implementations of the Product interface.
"""


class ConcreteProduct1(Product):
def operation(self) -> str:
return "{Result of the ConcreteProduct1}"


class ConcreteProduct2(Product):
def operation(self) -> str:
return "{Result of the ConcreteProduct2}"


def client_code(creator: Creator) -> None:
"""
The client code works with an instance of a concrete creator, albeit through
its base interface. As long as the client keeps working with the creator via
the base interface, you can pass it any creator's subclass.
"""

print(f"Client: I'm not aware of the creator's class, but it still works.\n"
f"{creator.some_operation()}", end="")


if __name__ == "__main__":
print("App: Launched with the ConcreteCreator1.")
client_code(ConcreteCreator1())
print("\n")

print("App: Launched with the ConcreteCreator2.")
client_code(ConcreteCreator2())

Output.txt: 执行结果

1
2
3
4
5
6
7
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}

在 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
# The Creator class declares the factory method that is supposed to return an
# object of a Product class. The Creator's subclasses usually provide the
# implementation of this method.
class Creator
# Note that the Creator may also provide some default implementation of the
# factory method.
def factory_method
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end

# Also note that, despite its name, the Creator's primary responsibility is
# not creating products. Usually, it contains some core business logic that
# relies on Product objects, returned by the factory method. Subclasses can
# indirectly change that business logic by overriding the factory method and
# returning a different type of product from it.
def some_operation
# Call the factory method to create a Product object.
product = factory_method

# Now, use the product.
result = "Creator: The same creator's code has just worked with #{product.operation}"

result
end
end

# Concrete Creators override the factory method in order to change the resulting
# product's type.
class ConcreteCreator1 < Creator
# Note that the signature of the method still uses the abstract product type,
# even though the concrete product is actually returned from the method. This
# way the Creator can stay independent of concrete product classes.
def factory_method
ConcreteProduct1.new
end
end

class ConcreteCreator2 < Creator
# @return [ConcreteProduct2]
def factory_method
ConcreteProduct2.new
end
end

# The Product interface declares the operations that all concrete products must
# implement.
class Product
# return [String]
def operation
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end

# Concrete Products provide various implementations of the Product interface.
class ConcreteProduct1 < Product
# @return [String]
def operation
'{Result of the ConcreteProduct1}'
end
end

class ConcreteProduct2 < Product
# @return [String]
def operation
'{Result of the ConcreteProduct2}'
end
end

# The client code works with an instance of a concrete creator, albeit through
# its base interface. As long as the client keeps working with the creator via
# the base interface, you can pass it any creator's subclass.
def client_code(creator)
print "Client: I'm not aware of the creator's class, but it still works.\n"\
"#{creator.some_operation}"
end

puts 'App: Launched with the ConcreteCreator1.'
client_code(ConcreteCreator1.new)
puts "\n\n"

puts 'App: Launched with the ConcreteCreator2.'
client_code(ConcreteCreator2.new)

output.txt: 执行结果

1
2
3
4
5
6
7
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}

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

/// The Creator protocol declares the factory method that's supposed to return a
/// new object of a Product class. The Creator's subclasses usually provide the
/// implementation of this method.
protocol Creator {

/// Note that the Creator may also provide some default implementation of
/// the factory method.
func factoryMethod() -> Product

/// Also note that, despite its name, the Creator's primary responsibility
/// is not creating products. Usually, it contains some core business logic
/// that relies on Product objects, returned by the factory method.
/// Subclasses can indirectly change that business logic by overriding the
/// factory method and returning a different type of product from it.
func someOperation() -> String
}

/// This extension implements the default behavior of the Creator. This behavior
/// can be overridden in subclasses.
extension Creator {

func someOperation() -> String {
// Call the factory method to create a Product object.
let product = factoryMethod()

// Now, use the product.
return "Creator: The same creator's code has just worked with " + product.operation()
}
}

/// Concrete Creators override the factory method in order to change the
/// resulting product's type.
class ConcreteCreator1: Creator {

/// Note that the signature of the method still uses the abstract product
/// type, even though the concrete product is actually returned from the
/// method. This way the Creator can stay independent of concrete product
/// classes.
public func factoryMethod() -> Product {
return ConcreteProduct1()
}
}

class ConcreteCreator2: Creator {

public func factoryMethod() -> Product {
return ConcreteProduct2()
}
}

/// The Product protocol declares the operations that all concrete products must
/// implement.
protocol Product {

func operation() -> String
}

/// Concrete Products provide various implementations of the Product protocol.
class ConcreteProduct1: Product {

func operation() -> String {
return "{Result of the ConcreteProduct1}"
}
}

class ConcreteProduct2: Product {

func operation() -> String {
return "{Result of the ConcreteProduct2}"
}
}


/// The client code works with an instance of a concrete creator, albeit through
/// its base protocol. As long as the client keeps working with the creator via
/// the base protocol, you can pass it any creator's subclass.
class Client {
// ...
static func someClientCode(creator: Creator) {
print("Client: I'm not aware of the creator's class, but it still works.\n"
+ creator.someOperation())
}
// ...
}

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

func testFactoryMethodConceptual() {

/// The Application picks a creator's type depending on the
/// configuration or environment.

print("App: Launched with the ConcreteCreator1.")
Client.someClientCode(creator: ConcreteCreator1())

print("\nApp: Launched with the ConcreteCreator2.")
Client.someClientCode(creator: ConcreteCreator2())
}
}

Output.txt: 执行结果

1
2
3
4
5
6
7
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}

真实世界示例

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

class FactoryMethodRealWorld: XCTestCase {

func testFactoryMethodRealWorld() {

let info = "Very important info of the presentation"

let clientCode = ClientCode()

/// Present info over WiFi
clientCode.present(info: info, with: WifiFactory())

/// Present info over Bluetooth
clientCode.present(info: info, with: BluetoothFactory())
}
}

protocol ProjectorFactory {

func createProjector() -> Projector

func syncedProjector(with projector: Projector) -> Projector
}

extension ProjectorFactory {

/// Base implementation of ProjectorFactory

func syncedProjector(with projector: Projector) -> Projector {

/// Every instance creates an own projector
let newProjector = createProjector()

/// sync projectors
newProjector.sync(with: projector)

return newProjector
}
}

class WifiFactory: ProjectorFactory {

func createProjector() -> Projector {
return WifiProjector()
}
}

class BluetoothFactory: ProjectorFactory {

func createProjector() -> Projector {
return BluetoothProjector()
}
}

protocol Projector {

/// Abstract projector interface

var currentPage: Int { get }

func present(info: String)

func sync(with projector: Projector)

func update(with page: Int)
}

extension Projector {

/// Base implementation of Projector methods

func sync(with projector: Projector) {
projector.update(with: currentPage)
}
}

class WifiProjector: Projector {

var currentPage = 0

func present(info: String) {
print("Info is presented over Wifi: \(info)")
}

func update(with page: Int) {
/// ... scroll page via WiFi connection
/// ...
currentPage = page
}
}

class BluetoothProjector: Projector {

var currentPage = 0

func present(info: String) {
print("Info is presented over Bluetooth: \(info)")
}

func update(with page: Int) {
/// ... scroll page via Bluetooth connection
/// ...
currentPage = page
}
}

private class ClientCode {

private var currentProjector: Projector?

func present(info: String, with factory: ProjectorFactory) {

/// Check wheater a client code already present smth...

guard let projector = currentProjector else {

/// 'currentProjector' variable is nil. Create a new projector and
/// start presentation.

let projector = factory.createProjector()
projector.present(info: info)
self.currentProjector = projector
return
}

/// Client code already has a projector. Let's sync pages of the old
/// projector with a new one.

self.currentProjector = factory.syncedProjector(with: projector)
self.currentProjector?.present(info: info)
}
}

Output.txt: 执行结果

1
2
Info is presented over Wifi: Very important info of the presentation
Info is presented over Bluetooth: Very important info of the presentation

在 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
/**
* The Creator class declares the factory method that is supposed to return an
* object of a Product class. The Creator's subclasses usually provide the
* implementation of this method.
*/
abstract class Creator {
/**
* Note that the Creator may also provide some default implementation of the
* factory method.
*/
public abstract factoryMethod(): Product;

/**
* Also note that, despite its name, the Creator's primary responsibility is
* not creating products. Usually, it contains some core business logic that
* relies on Product objects, returned by the factory method. Subclasses can
* indirectly change that business logic by overriding the factory method
* and returning a different type of product from it.
*/
public someOperation(): string {
// Call the factory method to create a Product object.
const product = this.factoryMethod();
// Now, use the product.
return `Creator: The same creator's code has just worked with ${product.operation()}`;
}
}

/**
* Concrete Creators override the factory method in order to change the
* resulting product's type.
*/
class ConcreteCreator1 extends Creator {
/**
* Note that the signature of the method still uses the abstract product
* type, even though the concrete product is actually returned from the
* method. This way the Creator can stay independent of concrete product
* classes.
*/
public factoryMethod(): Product {
return new ConcreteProduct1();
}
}

class ConcreteCreator2 extends Creator {
public factoryMethod(): Product {
return new ConcreteProduct2();
}
}

/**
* The Product interface declares the operations that all concrete products must
* implement.
*/
interface Product {
operation(): string;
}

/**
* Concrete Products provide various implementations of the Product interface.
*/
class ConcreteProduct1 implements Product {
public operation(): string {
return '{Result of the ConcreteProduct1}';
}
}

class ConcreteProduct2 implements Product {
public operation(): string {
return '{Result of the ConcreteProduct2}';
}
}

/**
* The client code works with an instance of a concrete creator, albeit through
* its base interface. As long as the client keeps working with the creator via
* the base interface, you can pass it any creator's subclass.
*/
function clientCode(creator: Creator) {
// ...
console.log('Client: I\'m not aware of the creator\'s class, but it still works.');
console.log(creator.someOperation());
// ...
}

/**
* The Application picks a creator's type depending on the configuration or
* environment.
*/
console.log('App: Launched with the ConcreteCreator1.');
clientCode(new ConcreteCreator1());
console.log('');

console.log('App: Launched with the ConcreteCreator2.');
clientCode(new ConcreteCreator2());

Output.txt: 执行结果

1
2
3
4
5
6
7
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}

概念示例

由于 Go 中缺少类和继承等 OOP 特性, 所以无法使用 Go 来实现经典的工厂方法模式。 不过, 我们仍然能实现模式的基础版本, 即简单工厂。

在本例中, 我们将使用工厂结构体来构建多种类型的武器。

首先, 我们来创建一个名为 i­Gun的接口, 其中将定义一支枪所需具备的所有方法。 然后是实现了 iGun 接口的 gun枪支结构体类型。 两种具体的枪支—— ak47musket火枪 ——两者都嵌入了枪支结构体, 且间接实现了所有的 i­Gun方法。

gun­Factory枪支工厂结构体将发挥工厂的作用, 即通过传入参数构建所需类型的枪支。 main.go 则扮演着客户端的角色。 其不会直接与 ak47musket进行互动, 而是依靠 gun­Factory来创建多种枪支的实例, 仅使用字符参数来控制生产。

工厂方法

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
//工厂方法模式
package factorymethod

import "fmt"

//意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
//解决:主要解决接口选择的问题

//不足:选择判断问题还是存在的。也就是说,工厂方法把简单的工厂内部逻辑判断转移到了客户端来运行。

//实例:实现一个鼠标工厂的实现的接口,不同品牌的鼠标工厂实现它

//鼠标接口
type IMouse interface {
SayMouseBrand()
}

//戴尔鼠标
type DellMouse struct{}

func (d DellMouse) SayMouseBrand() {
fmt.Println("Dell Mouse")
}

//惠普鼠标
type HpMouse struct{}

func (h HpMouse) SayMouseBrand() {
fmt.Println("Hp Mouse")
}

//鼠标工厂接口
type IMouseFactory interface {
Create() IMouse
}

//戴尔鼠标工厂实现
type DellMouseFactory struct{}

func (d DellMouseFactory) Create() IMouse {
return DellMouse{}
}

//惠普鼠标工厂
type HpMouseFactory struct{}

func (h HpMouseFactory) Create() IMouse {
return HpMouse{}
}


func main() {
var dmf DellMouseFactory
dm := dmf.Create()
dm.SayMouseBrand()
//OutPut:
//Dell Mouse

var hmf HpMouseFactory
hm := hmf.Create()
hm.SayMouseBrand()
//OutPut:
//Hp Mouse
}

简单工厂模式,不是23种之一

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
//简单工厂模式,不是23种之一
package simplefactory

import "fmt"

//意图:将对象的创建过程进行了封装,用户不需要知道具体的创建过程,只需要调用工厂类获取对象即可
//解决:创建过程的复杂性

//不足:简单工厂的写法是通过switch-case来判断对象创建过程的。在实际使用过程中,违背了 开放-关闭原则,当然有些情况下可以通过反射调用来弥补这种不足。

//实例:实现一个生产鼠标工厂的类,通过输入参数不同,实例化不同品牌的鼠标工厂

//鼠标接口
type IMouse interface {
SayMouseBrand()
}

//戴尔鼠标
type DellMouse struct{}

func (d DellMouse) SayMouseBrand() {
fmt.Println("Dell Mouse")
}

//惠普鼠标
type HpMouse struct{}

func (h HpMouse) SayMouseBrand() {
fmt.Println("Hp Mouse")
}

//实例化鼠标工厂
type MouseFactory struct{}

func (m MouseFactory) CreateMouse(i int) IMouse {
switch i {
case 0: //戴尔
return new(DellMouse)
case 1: //惠普
return new(HpMouse)
}
return nil
}

func main() {
var mf MouseFactory
m := mf.CreateMouse(0) //戴尔鼠标
m.SayMouseBrand()
m = mf.CreateMouse(1) //惠普鼠标
m.SayMouseBrand()
//OutPut:
//Dell Mouse
//Hp Mouse
}