定义
简单工厂模式
- 专门定义一个类来负责创建其他类的实例,根据参数的不同创建不同类的实例,被创建的实例通常具有共同的父类,这个模式叫简单工厂模式(Simple Factory Pattern) 。
- 简单工厂模式又称为静态工厂方法模式。之所以叫“静态”,是因为在很多静态语言(如 Java、C++)中方法通常被定义成一个静态(static)方法,这样便可通过类名来直接调用方法。
- 这是最简单的一个版本,只有一个工厂类 SimpleFactory,类中有一个静态的创建方法 createProduct,该方法根据参数传递过来的类型值(type)或名称(name)来创建具体的产品(子类)对象。
- 定义:定义一个创建对象(实例化对象)的接口,通过参数来决定创建哪个类的实例。
- 类图
- 优缺点
- 优点:
- 实现简单、结构清晰。
- 抽象出一个专门的类来负责某类对象的创建,分割出创建的职责,不能直接创建具体的对象,只需传入适当的参数即可。
- 使用者可以不关注具体对象的类名称,只需知道传入什么参数可以创建哪些需要的对象。
- 缺点:
- 不易拓展,一旦添加新的产品类型,就不得不修改工厂的创建逻辑。不符合“开放 – 封闭”原则,如果要增加或删除一个产品类型,就要修改 switch … case …(或 if … else …)的判断代码。
- 当产品类型较多时,工厂的创建逻辑可能过于复杂,switch … case …(或 if … else …)判断会变得非常多。一旦出错可能造成所有产品创建失败,不利于系统的维护。
- 应用场景
- 产品具有明显的继承关系,且产品的类型不太多。
- 所有的产品具有相同的方法和类似的属性,使用者不关心具体的类型,只希望传入合适的参数能返回合适的对象。
工厂方法模式
- 工厂方法模式是简单工厂模式的一个升级版本,为解决简单工厂模式不符合“开放 – 封闭”原则的问题,我们对 SimpleFactory 进行了一个拆分,抽象出一个父类 Factory,并增加多个子类分别负责创建不同的具体产品。
- 定义:定义一个创建对象(实例化对象)的接口,让子类来决定创建哪个类的实例。工厂方法使一个类的实例化延迟到其子类。
- 类图
- Product 是要创建的产品的抽象类,ProductA 和 ProductB 是具体的产品类型。Factory 是所有工厂的抽象类,负责定义统一的接口。ProductAFactory 和 ProductBFactory 是具体的工厂类,分别负责产品 ProductA 和 ProductB 的创建。
- 优缺点
- 优点:
- 解决了简单工厂模式不符合“开放 – 封闭”原则的问题,使程序更容易拓展。
- 实现简单。
- 缺点:
- 对于有多种分类的产品,或具有二级分类的产品,工厂方法模式并不适用。
- 多种分类:如我们有一个电子白板程序,可以绘制各种图形,那么画笔的绘制功能可以理解为一个工厂,而图形可以理解为一种产品;图形可以根据形状分为直线、矩形、椭圆等,也可以根据颜色分为红色图形、绿色图形、蓝色图形等。
- 二级分类:如一个家电工厂,它可能同时生产冰箱、空调和洗衣机,那么冰箱、空调、洗衣机属于一级分类;而洗衣机又可分为高效型的和节能型的,高效型洗衣机和节能型洗衣机就属于二级分类。
- 应用场景
- 客户端不知道它所需要的对象的类。
- 工厂类希望通过其子类来决定创建哪个具体类的对象。因为工厂方法模式简单且易拓展,因此在项目中应用得非常广泛,在很多标准库和开源项目中都能看到它的影子。
抽象工厂模式
- 抽象工厂模式是工厂方法模式的升级版本,工厂方法模式不能解决具有二级分类的产品的创建问题,抽象工厂模式就是用来解决这一问题的。
- 定义:提供一个创建一系列相关或相互依赖的对象的接口,而无须指定它们的具体类。
- 类图
- 抽象工厂模式适用于有多个系列且每个系列有相同子分类的产品。我们定义一个抽象的工厂类 AbstractFactory,AbstractFactory 中定义生产每个系列产品的方法;而两个具体的工厂实现类 Factory1 和 Factory2 分别生产子分类 1 的每一系列产品和子分类 2 的每一系列产品。
- 优缺点
- 优点
- 缺点
- 如果产品的分类超过二级,如三级甚至更多级,抽象工厂模式将会变得非常臃肿。
- 不能解决产品有多种分类、多种组合的问题。
- 应用场景
- 系统中有多于一个的产品族,而每次只使用其中某一产品族。
- 产品等级结构稳定,设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构。
进一步思考
- 如果产品出现三级甚至更多级分类怎么办?
- 如果程序中出现了三级分类的对象,就需要重新审视一下你的设计,看一下有些类是不是可以进行归纳、抽象合并。如果实际的应用场景中确实有三级甚至更多级分类,建议你不要使用工厂模式了,直接交给每一个具体的产品类自己去创建吧!因为超过三级(含三级)以上分类,会使工厂类变得非常臃肿而难以维护,开发成本也会急剧增加。模式是死的,人是活的,不要为了使用设计模式而使用设计模式!
- 如果产品有多种分类、多种组合怎么办?
- 如果产品有多种分类,就不能单独使用工厂模式了,需要结合其他的设计模式进行优化。如 15.5 节的白板程序,既有形状的分类又有颜色的分类,可以结合桥接模式(参见 20.2 节)一起使用,用桥接模式来定义产品,再用工厂模式来创建产品。
实战应用
from abc import ABCMeta, abstractmethod
# 引入ABCMeta和abstractmethod来定义抽象类和抽象方法
from enum import Enum
# Python3.4 之后支持枚举Enum的语法
class PenType(Enum):
"""画笔类型"""
PenTypeLine = 1
PenTypeRect = 2
PenTypeEllipse = 3
class Pen(metaclass=ABCMeta):
"""画笔"""
def __init__(self, name):
self.__name = name
@abstractmethod
def getType(self):
pass
def getName(self):
return self.__name
class LinePen(Pen):
"""直线画笔"""
def __init__(self, name):
super().__init__(name)
def getType(self):
return PenType.PenTypeLine
class RectanglePen(Pen):
"""矩形画笔"""
def __init__(self, name):
super().__init__(name)
def getType(self):
return PenType.PenTypeRect
class EllipsePen(Pen):
"""椭圆画笔"""
def __init__(self, name):
super().__init__(name)
def getType(self):
return PenType.PenTypeEllipse
class PenFactory:
"""画笔工厂类"""
def __init__(self):
"定义一个字典(key:PenType,value:Pen)来存放对象,确保每一个类型只会有一个对象"
self.__pens = {}
def getSingleObj(self, penType, name):
"""获得唯一实例的对象"""
def createPen(self, penType):
"""创建画笔"""
if self.__pens.get(penType) is None:
# 如果该对象不存在,则创建一个对象并存到字典中
if penType == PenType.PenTypeLine:
pen = LinePen("直线画笔")
elif penType == PenType.PenTypeRect:
pen = RectanglePen("矩形画笔")
elif penType == PenType.PenTypeEllipse:
pen = EllipsePen("椭圆画笔")
else:
pen = Pen("")
self.__pens[penType] = pen
# 否则直接返回字典中的对象
return self.__pens[penType]
def testPenFactory():
factory = PenFactory()
linePen = factory.createPen(PenType.PenTypeLine)
print("创建了 %s,对象id:%s, 类型:%s" % (linePen.getName(), id(linePen), linePen.getType()))
rectPen = factory.createPen(PenType.PenTypeRect)
print("创建了 %s,对象id:%s, 类型:%s" % (rectPen.getName(), id(rectPen), rectPen.getType()))
rectPen2 = factory.createPen(PenType.PenTypeRect)
print("创建了 %s,对象id:%s, 类型:%s" % (rectPen2.getName(), id(rectPen2), rectPen2.getType()))
ellipsePen = factory.createPen(PenType.PenTypeEllipse)
print("创建了 %s,对象id:%s, 类型:%s" % (ellipsePen.getName(), id(ellipsePen), ellipsePen.getType()))
testPenFactory()