抽象工厂模式

<image>

在软件工程中，抽象工厂模式（abstract factory pattern），是一种软件设计模式。抽象工厂模式提供了一种方式，可以将一组具有同一主题的单独的工厂封装起来。抽象工厂模式将一组对象的实现细节与他们的一般使用分离开来。

在正常使用中，客户端程序需要创建抽象工厂的具体实现，然后使用抽象工厂作为接口来创建这一主题的具体对象。客户端程序不需要知道（或关心）它从这些内部的工厂方法中获得对象的具体类型，因为客户端程序仅使用这些对象的通用接口。

## 概述

工厂是创建产品（对象）的地方，其目的是将产品的创建与产品的使用分离。抽象工厂模式的目的，是将若干抽象产品的接口与不同主题产品的具体实现分离开。这样就能在增加新的具体工厂的时候，不用修改引用抽象工厂的客户端代码。

抽象工厂模式的实质是“提供接口，创建一系列相关或独立的对象，而不指定这些对象的具体类。”使用抽象工厂模式，能够在具体工厂变化的时候，不用修改使用工厂的客户端代码，甚至是在运行时。然而，使用这种模式或者相似的设计模式，可能给编写代码带来不必要的复杂性和额外的工作。正确使用设计模式能够抵消这样的“额外工作”。

举个例子来说，比如一个抽象工厂类叫做`DocumentCreator`（文档创建器），此类提供创建若干种产品的接口，包括`createLetter()`（创建信件）和`createResume()`（创建简历）。其中，`createLetter()`返回一个`Letter`（信件），`createResume()`返回一个`Resume`（简历）。

系统中还有一些`DocumentCreator`的具体实现类，包括`FancyDocumentCreator`和`ModernDocumentCreator`。这两个类对`DocumentCreator`的两个方法分别有不同的实现，用来创建不同的“信件”和“简历”（用`FancyDocumentCreator`的实例可以创建`FancyLetter`和`FancyResume`，用`ModernDocumentCreator`的实例可以创建`ModernLetter`和`ModernResume`）。这些具体的“信件”和“简历”类均继承自抽象类，即`Letter`和`Resume`类。

客户端需要创建“信件”或“简历”时，先要得到一个合适的`DocumentCreator`实例，然后调用它的方法。一个工厂中创建的每个对象都是同一个主题的（“fancy”或者“modern”）。客户端程序只需要知道得到的对象是“信件”或者“简历”，而不需要知道具体的主题，因此客户端程序从抽象工厂`DocumentCreator`中得到了`Letter`或`Resume`类的引用，而不是具体类的对象引用。

## 结构

<image>

在上面的UML类图中，`Client`类要求`ProductA`和`ProductB`对象，但它不直接的实例化`ProductA1`和`ProductB1`类。`Client`转而提及`AbstractFactory`接口来创建对象，这使得`Client`独立于如何创建对象（哪个具体具体类被实例化）。`Factory1`类通过实例化`ProductA1`和`ProductB1`类来实现`AbstractFactory`接口。

UML序列图展示了运行时交互：`Client`对象调用`createProductA()`于`Factory1`对象之上，它创建并返回一个`ProductA1`对象。此后，`Client`调用`createProductB()`于`Factory1`之上，它创建并返回一个`ProductB1`对象。

## 使用

在以下情况可以考虑使用抽象工厂模式：

- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品系列中的一个来配置时。
- 需要强调一系列相关的产品对象的设计以便进行联合使用时。
- 提供一个产品类库，而只想显示它们的接口而不是实现时。

抽象工厂模式的优点有：具体产品从客户代码中被分离出来，容易改变产品的系列，将一个系列的产品族统一到一起创建。其缺点是：在产品族中扩展新的产品是很困难的，它需要修改抽象工厂的接口。

具体的工厂决定了创建对象的具体类型，而且工厂就是对象实际创建的地方（比如在C++中，用“new”操作符创建对象）。然而，抽象工厂只返回一个指向创建的对象的抽象引用（或指针）。这样，客户端程序调用抽象工厂引用的方法，由具体工厂完成对象创建，然后客户端程序得到的是抽象产品的引用。如此使客户端代码与对象的创建分离开来。

因为工厂仅仅返回一个抽象产品的引用（或指针），所以客户端程序不知道（也不会牵绊于）工厂创建对象的具体类型。然而，工厂知道具体对象的类型；例如，工厂可能从配置文件中读取某种类型。这时，客户端没有必要指定具体类型，因为已经在配置文件中指定了。通常，这意味着：

- 客户端代码不知道任何具体类型，也就没必要引入任何相关的头文件或类定义。客户端代码仅仅处理抽象类型。工厂确实创建了具体类型的对象，但是客户端代码仅使用这些对象的抽象接口来访问它们。
- 如果要增加一个具体类型，只需要修改客户端代码使用另一个工厂即可，而且这个修改通常只是一个文件中的一行代码。不同的工厂创建不同的具体类型的对象，但是和以前一样返回一个抽象类型的引用（或指针），因此客户端代码的其他部分不需要任何改动。这样比修改客户端代码创建新类型的对象简单多了。如果是后者的话，需要修改代码中每一个创建这种对象的地方（而且需要注意的是，这些地方都知道对象的具体类型，而且需要引入具体类型的头文件或类定义）。如果所有的工厂对象都存储在全局的单例对象中，所有的客户端代码到这个单例中访问需要的工厂，那么，更换工厂就非常简单了，仅仅需要更改这个单例对象即可。

## 代码举例

假设我们有两种产品接口Button和Border，每一种产品都支持多种系列，比如Mac系列和Windows系列。这样每个系列的产品分别是MacButton、WinButton、MacBorder和WinBorder。为了可以在运行时刻创建一个系列的产品族，我们可以为每个系列的产品族建立一个工厂MacFactory和WinFactory。每个工厂都有两个方法CreateButton和CreateBorder并返回对应的产品，可以将这两个方法抽象成一个接口AbstractFactory。这样在运行时刻我们可以选择创建需要的产品系列。

<image>

在抽象工厂模式中，通常一个工厂能够创建若干种不同类型的对象，为了简洁起见，以上类图仅仅展示了创建一个类型对象的工厂。`GuiFactory`接口中的`createButton`方法返回`Button`类型的对象。返回`Button`的哪种实现依赖于使用`GuiFactory`的哪种实现。

### Java

Java的例子：

```java
//定义接口
public interface Button {}
public interface Border {}

//实现抽象类
public class MacButton implements Button {}
public class MacBorder implements Border {}
public class WinButton implements Button {}
public class WinBorder implements Border {}

//实现工厂
public class MacFactory {
	public static Button createButton() {
	    return new MacButton();
	}
	public static Border createBorder() {
	    return new MacBorder();
	}
}

//客户使用
public class WinFactory {
	public static Button createButton() {
	    return new WinButton();
	}
	public static Border createBorder() {
	    return new WinBorder();
	}
}
```

### Python

Python的例子：

```python
from abc import ABC, abstractmethod

class Button(ABC):
    @abstractmethod
    def paint(self): pass
    
class Border(ABC):
    @abstractmethod
    def paint(self): pass

class MacButton(Button):
    def paint(self):
        print("It is a MacButton.")

class MacBorder(Border):
    def paint(self):
        print("It is a MacBorder.")

class WinButton(Button):
    def paint(self):
        print("It is a WinButton.")

class WinBorder(Border):
    def paint(self):
        print("It is a WinBorder.")

class GUIFactory(ABC):
    @abstractmethod
    def create_button(self): pass
    @abstractmethod
    def create_border(self): pass

class MacFactory(GUIFactory):
    def create_button(self):
        return MacButton()
    def create_border(self):
        return MacBorder()

class WinFactory(GUIFactory):
    def create_button(self):
        return WinButton()
    def create_border(self):
        return WinBorder()
```

其执行：

```pycon
>>> factory1 = WinFactory()
>>> factory1.create_button().paint()
It is a WinButton.
>>> factory1.create_border().paint()
It is a WinBorder.
>>> factory2 = MacFactory()
>>> factory2.create_button().paint()
It is a MacButton.
>>> factory2.create_border().paint()
It is a MacBorder.
```

### C++

下面是基于《设计模式》书中前C++98实现的C++23实现迷路园游戏例子：

```c++
import std;

using std::array;
using std::shared_ptr;
using std::unique_ptr;
using std::vector;

enum class Direction: char {
    NORTH, 
    SOUTH, 
    EAST, 
    WEST
};

class MapSite {
public:
    virtual void enter() = 0;
    virtual ~MapSite() = default;
};

class Room: public MapSite {
private:
    int roomNumber;
    shared_ptr<array<MapSite, 4>> sides;
public:
    explicit Room(int n = 0): 
        roomNumber{n} {}

    ~Room() = default;

    Room& setSide(Direction d, MapSite* ms) {
        sides[static_cast<size_t>(d)] = std::move(ms);
        std::println("Room::setSide {} ms", d);
        return *this;
    }

    virtual void enter() override = 0;

    Room(const Room&) = delete;
    Room& operator=(const Room&) = delete;
};

class Wall: public MapSite {
public:
    explicit Wall(int n = 0):
        MapSite(n) {}

    ~Wall() = default;

    void enter() override {
        // ...
    }
};

class Door: public MapSite {
private:
    shared_ptr<Room> room1;
    shared_ptr<Room> room2;
public:
    explicit Door(int n = 0, shared_ptr<Room> r1 = nullptr, shared_ptr<Room> r2 = nullptr): 
        MapSite(n), room1{std::move(r1)}, room2{std::move(r2)} {}

    ~Door() = default;

    void enter() override {
        // ...
    }

    Door(const Door&) = delete;
    Door& operator=(const Door&) = delete;
};

class Maze {
private:
    vector<shared_ptr<Room>> rooms;
public:
    Maze() = default;
    ~Maze() = default;

    Maze& addRoom(shared_ptr<Room> r) {
        std::println("Maze::addRoom {}", reinterpret_cast<void*>(r.get()));
        rooms.push_back(std::move(r));
        return *this;
    }
  
    shared_ptr<Room> roomNo(int n) const {
        for (const Room& r: rooms) {
            // actual lookup logic here...
        }
        return nullptr;
    }
};

class MazeFactory {
public:
    MazeFactory() = default;

    virtual ~MazeFactory() = default;

    [[nodiscard]]
    unique_ptr<Maze> makeMaze() const {
        return std::make_unique<Maze>();
    }

    [[nodiscard]]
    shared_ptr<Wall> makeWall() const {
        return std::make_shared<Wall>();
    }

    [[nodiscard]]
    shared_ptr<Room> makeRoom(int n) const {
        return std::make_shared<Room>(new Room(n));
    }
    
    [[nodiscard]]
    shared_ptr<Door> makeDoor(shared_ptr<Room> r1, shared_ptr<Room> r2) const {
        return std::make_shared<Door>(std::move(r1), std::move(r2));
    }
};

// If createMaze is passed an object as a parameter to use to create rooms, walls, and doors, then you can change the classes of rooms, walls, and doors by passing a different parameter. This is an example of the Abstract Factory (99) pattern.

class MazeGame {
public:
    Maze() = default;
    ~Maze() = default;

    [[nodiscard]]
    unique_ptr<Maze> createMaze(MazeFactory& factory) {
        unique_ptr<Maze> maze = factory.makeMaze();
        shared_ptr<Room> r1 = factory.makeRoom(1);
        shared_ptr<Room> r2 = factory.makeRoom(2);
        shared_ptr<Door> door = factory.makeDoor(r1, r2);
        maze->addRoom(r1)
            .addRoom(r2)
                .setSide(Direction::NORTH, factory.makeWall())
                .setSide(Direction::EAST, door)
                .setSide(Direction::SOUTH, factory.makeWall())
                .setSide(Direction::WEST, factory.makeWall())
                .setSide(Direction::NORTH, factory.makeWall())
                .setSide(Direction::EAST, factory.makeWall())
                .setSide(Direction::SOUTH, factory.makeWall())
                .setSide(Direction::WEST, door);
        return maze;
    }
};

int main(int argc, char* argv[]) {
    MazeGame game;
    unique_ptr<Maze> maze = game.createMaze(MazeFactory());
}
```

程序的输出为:

```output
Maze::addRoom 0x1317ed0
Maze::addRoom 0x1317ef0
Room::setSide 0 0x1318340
Room::setSide 2 0x1317f10
Room::setSide 1 0x1318360
Room::setSide 3 0x1318380
Room::setSide 0 0x13183a0
Room::setSide 2 0x13183c0
Room::setSide 1 0x13183e0
Room::setSide 3 0x1317f10
```