Python 设计模式之抽象工厂模式

2019-11-24 / Java Python Design Pattern

每一个设计模式都是针对一定问题的解决方案。抽象工厂模式与工厂方法模式的最大区别在于:工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构。

在学习抽象工厂具体实例之前,应该明白两个重要的概念:产品族产品等级

所谓产品族是指位于不同产品等级结构中,功能相关联的产品组成的家族。比如 AMD 的主板、芯片组、CPU组成一个家族。Intel 的主板、芯片组、CPU 组成一个家族。而这两个家族都来自于三个产品等级:主板、芯片组、CPU。一个等级结构是由相同的结构的产品组成。示意图如下:

photo_2019-11-24_20-45-12

显然,每一个产品族中含有产品的数目,与产品等级结构的数目是相等的。产品的等级结构与产品族将产品按照不同方向划分,形成一个二维的坐标系。横轴表示产品的等级结构,纵轴表示产品族,上图共有两个产品族,分布于三个不同的产品等级结构中。只要指明一个产品所处的产品族以及它所属的等级结构,就可以唯一的确定这个产品。

上面所给出的三个不同的等级结构具有平行的结构。因此,如果采用工厂方法模式,就势必要使用三个独立的工厂等级结构来对付这三个产品等级结构。由于这三个产品等级结构的相似性,会导致三个平行的工厂等级结构。随着产品等级结构的数目增加,工厂方法模式所给出的工厂等级结构的数目也会随之增加。如下图:

photo_2019-11-24_20-45-15

那么,是否可以使用同一个工厂等级结构来对付这些相同或者极为相似的产品等级结构呢?当然可以的,而且这就是抽象工厂模式的好处。同一个工厂等级结构负责三个不同产品等级结构中的产品对象的创建。

photo_2019-11-24_20-48-51

可以看出,一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象。显然,这时候抽象工厂模式比简单工厂模式、工厂方法模式更有效率。对应于每一个产品族都有一个具体工厂。而每一个具体工厂负责创建属于同一个产品族,但是分属于不同等级结构的产品。

抽象工厂模式是对象的创建模式,它是工厂方法模式的进一步推广。

假设一个子系统需要一些产品对象,而这些产品又属于一个以上的产品等级结构。那么为了将消费这些产品对象的责任和创建这些产品对象的责任分割开来,可以引进抽象工厂模式。这样的话,消费产品的一方不需要直接参与产品的创建工作,而只需要向一个公用的工厂接口请求所需要的产品。

通过使用抽象工厂模式,可以处理具有相同(或者相似)等级结构中的多个产品族中的产品对象的创建问题。如下图所示:

photo_2019-11-24_20-55-55

由于这两个产品族的等级结构相同,因此使用同一个工厂族也可以处理两个产品族的创建问题,这就是抽象工厂模式。

根据产品角色的结构图,就不难给出工厂角色的结构设计图。

photo_2019-11-24_20-57-47

可以看出,每一个工厂角色都有两个工厂方法,分别负责创建分属于不同产品等级结构的产品对象。

photo_2019-11-24_21-00-03

抽象工厂的功能是为一系列相关对象或相互依赖的对象创建一个接口。一定要注意,这个接口的方法不是任意堆砌的,而是一系列相关或相互依赖的方法。比如上面例子中的主板和 CPU ,都是为了组装一台电脑的相关对象。不同的装机方案,代表一种具体的电脑系列。

photo_2019-11-24_21-03-52

由于抽象工厂定义的一系列对象通常是相关的相互依赖的,这些产品对象就构成了一个产品族,也就是抽象工厂定义了一个产品族。

这就带来非常大的灵活性,切换产品族的时候,只要提供不同的抽象工厂实现就可以了,也就是说现在是以一个产品族作为一个整体被切换。

photo_2019-11-24_21-07-09

在什么情况下应当使用抽象工厂模式

  1. 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。
  2. 这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
  3. 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。(比如:Intel 主板必须使用 Intel CPU、Intel 芯片组)
  4. 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

抽象工厂模式的起源

抽象工厂模式的起源或者最早的应用,是用于创建分属于不同操作系统的视窗构建。比如:命令按键(Button)与文字框(Text)都是视窗构建。在 UNIX 操作系统的视窗环境和 Windows 操作系统的视窗环境中,这两个构建有不同的本地实现,它们的细节有所不同。

在每一个操作系统中,都有一个视窗构建组成的构建家族。在这里就是 Button 和 Text 组成的产品族。而每一个视窗构建都构成自己的等级结构,由一个抽象角色给出抽象的功能描述,而由具体子类给出不同操作系统下的具体实现。

抽象工厂模式的优点

  • 分离接口和实现

    客户端使用抽象工厂来创建需要的对象,而客户端根本就不知道具体的实现是谁,客户端只是面向产品的接口编程而已,也就是说,客户端从具体的产品实现中解耦。

  • 使切换产品族变得容易

    因为一个具体的工厂实现代表的是一个产品族,比如上面例子的从 Intel 系列到 AMD 系列只需要切换一下具体工厂。

抽象工厂模式的缺点

  • 不太容易扩展新的产品

    如果需要给整个产品族添加一个新的产品,那么 就需要修改抽象工厂,这样就会导致修改所有的工厂实现类。

class AbstractCPU(object):
    series_name = ''


class AbstractFactory(object):
    computer_name = ""

    def createCPU(self):
        raise NotImplementedError

    def createMainBoard(self):
        raise NotImplementedError


class AbstractMainBoard(object):
    series_name = ''


class AMDCPU(AbstractCPU):
    def __init__(self, series):
        self.series_name = series


class AMDMainBoard(AbstractMainBoard):
    def __init__(self, series):
        self.series_name = series


class AMDFactory(AbstractFactory):
    computer_name = 'AMD 4 computer'

    def createCPU(self):
        return AMDCPU('amd444')

    def createMainBoard(self):
        return AMDMainBoard('amd400')


class IntelCPU(AbstractCPU):
    def __init__(self, series):
        self.series_name = series


class IntelMainBoard(AbstractMainBoard):
    def __int__(self):
        pass

    def __init__(self, series):
        self.series_name = series


class IntelFactory(AbstractFactory):
    computer_name = 'Intel I7-series computer'

    def createCPU(self):
        return IntelCPU('I7-6500')

    def createMainBoard(self):
        return IntelMainBoard('Intel-6000')


class ComputerEngineer(object):
    def makeComputer(self, factory_obj):
        self.prepareHardwares(factory_obj)

    def prepareHardwares(self, factory_obj):
        self.cpu = factory_obj.createCPU()
        self.mainboard = factory_obj.createMainBoard()

        info = """-------------- computer %s info:\ncpu: %s\nmainboard: %s""" % (
            factory_obj.computer_name, self.cpu.series_name, self.mainboard.series_name
        )
        print(info)


if __name__ == '__main__':
    engineer = ComputerEngineer()
    intel_factory = IntelFactory()
    engineer.makeComputer(intel_factory)

    amd_factory = AMDFactory()
    engineer.makeComputer(amd_factory)