跳转到内容

抽象工厂模式

本页使用了标题或全文手工转换
维基百科,自由的百科全书
統一塑模語言中的類別圖來表示抽象工廠

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

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

概述

[编辑]

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

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

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

系统中还有一些DocumentCreator的具体实现类,包括FancyDocumentCreatorModernDocumentCreator。这两个类对DocumentCreator的两个方法分别有不同的实现,用来创建不同的“信件”和“简历”(用FancyDocumentCreator的实例可以创建FancyLetterFancyResume,用ModernDocumentCreator的实例可以创建ModernLetterModernResume)。这些具体的“信件”和“简历”类均继承自抽象类,即LetterResume类。

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

结构

[编辑]
抽象工厂模式的样例UML类图和序列图[2]

在上面的UML类图中,Client类要求ProductAProductB对象,但它不直接的实例化ProductA1ProductB1类。Client转而提及AbstractFactory接口来创建对象,这使得Client独立于如何创建对象(哪个具体具体类被实例化)。Factory1类通过实例化ProductA1ProductB1类来实现AbstractFactory接口。

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

使用

[编辑]

在以下情况可以考虑使用抽象工廠模式:

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

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

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

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

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

代码举例

[编辑]

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

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

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的例子:

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

其执行:

>>> 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实现迷路园游戏例子:

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

程序的输出为:

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

参考文献

[编辑]
  1. ^ Gamma, Erich; Richard Helm, Ralph Johnson, John M. Vlissides. Design Patterns: Abstract Factory (HTML). informIT. 2009-10-23 [2012-05-16]. (原始内容存档于2021-03-22) (英语). Object Creational: Abstract Factory: Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes. 
  2. ^ The Abstract Factory design pattern - Structure and Collaboration. w3sDesign.com. [2017-08-12]. 
  3. ^ Veeneman, David. Object Design for the Perplexed (HTML). The Code Project. 2009-10-23 [2012-05-16]. (原始内容存档于2011-02-21) (英语). The factory insulates the client from changes to the product or how it is created, and it can provide this insulation across objects derived from very different abstract interfaces. 
  4. ^ 4.0 4.1 Abstract Factory: Implementation (HTML). OODesign.com. [2012-05-16]. (原始内容存档于2021-03-10) (英语). 

参见

[编辑]