抽象工厂模式属于创建型设计模式,一般用来创建复杂的对象,这种对象由许多小对象组成,这些小对象都属于某个特定的类别。
从一个例子谈起
常见的例子是在家具商城中,有家具产品:椅子和沙发等等;这些产品同时有中式、日式等风格。如果用户期望一批日式风格的家具,你就得给它一批日式风格的家具。这里中式风格的家具生产厂
就是一个复杂对象,而具体的中式椅子
就是小对象。
假设现在客户端代码希望通过调用代码,生产出一批家具,这批家具必须风格一致。没有使用这种设计模式前,我们可能会这样实现代码。
class ChinaSofa:
@classmethod
def create_sofa(cls):
return ChinaSofa()
def __str__(self):
return "china style sofa"
class ChinaChair:
@classmethod
def create_chair(cls):
return ChinaChair()
def __str__(self):
return "china style chair"
class JapanSofa:
@classmethod
def create_sofa(cls):
return JapanSofa()
def __str__(self):
return "japan style sofa"
class JapanChair:
@classmethod
def create_chair(cls):
return JapanChair()
def __str__(self):
return "japan style chair"
def main():
print(ChinaSofa.create_sofa())
print(ChinaChair.create_chair())
main()
这里 main
函数可以理解为客户端代码,我们看到在生产某种风格的产品时,代码需要知道用 ChinaSofa
类和 ChinaChair
print(ChinaSofa.create_sofa())
print(ChinaChair.create_chair())
这里有两个明显的缺点:
- 如果在产品非常多的情况下,使用者可能会在代码中混入其他风格的类来生成产品。这意味着需要使用者很大的心智负担
- 如果现在增加一种欧式风格的沙发和椅子,我们就需要修改
main
函数的代码,即代码间存在耦合
作为客户端代码的开发者会更希望自己不需要关注生产中式风格的椅子用哪个类、日式风格的椅子又是哪个类。对他来说理想的行为应该是:
基于这样的设想,可以预见我们的main
函数将是这样的:
def main():
# 风格的选择可以作为配置文件或者函数参数传入
factory=ChinaStyle()
print(factory.create_sofa())
print(factory.create_chair())
在这种调用形式下,我们解决了上面提到的两个缺点,那么该如何实现呢?
抽象工厂模式的组件构成
在进入实现之前,需要了解下抽象工厂模式由哪些组件组成:
-
抽象工厂
抽象工厂是一个高层次的蓝图,它定义了创建相关对象族的一系列规则,而无需指定具体的类。它声明了一系列方法,每个方法负责创建一种特定类型的对象,并确保具体工厂遵循一个通用接口,从而提供一种一致的方式来生成相关的对象集。
-
具体的工厂
具体工厂实现抽象工厂定义的接口函数。它包含在一个系列中创建特定对象实例的逻辑。一般会存在多个具体工厂,每个工厂都是为生产一个不同的相关对象系列而量身定制的。
-
抽象的产品
抽象产品通过定义一组通用方法或属性来表示相关对象的系列。它是一个系列中所有具体产品都必须遵守的抽象或接口类型,并为具体产品的互换使用提供了统一的方式。
-
具体的产品
它们是由具体工厂创建的对象的实际实例。它们实现抽象产品中声明的方法,确保族内的一致性,并属于相关对象的特定类别或族。
-
客户端
客户利用抽象工厂创建对象系列,无需指定具体类型,并通过抽象产品提供的抽象接口与对象交互。客户可通过更改具体工厂实例,灵活地在对象系列之间无缝切换。
具体的调用逻辑
实际上我们可以忽略实现
抽象产品
根据抽象工厂模式实现
根据上面的组件介绍,我们可以对前文的例子进行对号入座:
- 客户端根据期望,用特定的工厂类生成工厂实例。这些工厂类继承自一个抽象的工厂类;
- 抽象工厂类定义了一系列可以生成产品实例的抽象函数,因此特定的工厂类通过重写这些函数,使得他们可以生成自己系列的产品对象
根据期望的行为给出接口的关系图:
接下来就是照着这张图实现:
from abc import ABC, abstractmethod
class FurnitureFactory(ABC):
@abstractmethod
def create_sofa(self):
pass
@abstractmethod
def create_chair(self):
pass
class ChinaStyle(FurnitureFactory):
def create_sofa(self):
return ChinaStyleSofa()
def create_chair(self):
return ChinaStyleChair()
class JapanStyle(FurnitureFactory):
def create_sofa(self):
return JapanStyleSofa()
def create_chair(self):
return JapanStyleChair()
class Sofa(ABC):
def can_sleep():
return "the man can sleep on it"
class ChinaStyleSofa(Sofa):
def __str__(self):
return "china style sofa"
class JapanStyleSofa():
def __str__(self):
return "japan style sofa"
class Chair(ABC):
def can_sit():
return "the man can sit down"
class ChinaStyleChair(Chair):
def __str__(self):
return "china style chair"
class JapanStyleChair(Chair):
def __str__(self):
return "japan style chair"
def main():
factory = ChinaStyle()
print(factory.create_sofa())
print(factory.create_chair())
main()
抽象工厂模式的缺点
- 对比这两段代码,可以发现这种设计模式下的代码更加复杂,它实现了很多的抽象工厂和抽象产品接口,而在这个简单的例子中显得有些多余。因此对于简单的系统而言,这种模式有时并不适合,它带来的代码复杂度大过了它带来的优点。