三、工厂模式:建立创建对象的工厂
工厂模式可以说是最常用的设计模式
本章主要介绍创建型设计模式:工厂模式
本章主题
- 了解简单工厂设计模式
- 讨论工厂方法和抽象工厂方法及其差异
- 利用Python 代码实现真实场景
- 讨论模式的优缺点并进行相应的比较
1. 了解工厂模式
面向对象编程中,‘工厂’ 表示一个负责创建其他类型对象的类
工厂与客户端的关系:
- 工厂的类创建一个对象以及与这个对象关联的多个方法
- 客户端使用某些参数调用此方法
- 工厂会据此创建所需类型的对象,然后将它们返回给客户端
那么问题是为什么不能是客户端直接创建对象呢?工厂的优点在于:
- 松耦合,即对象的创建可以独立于类的实现
- 客户端无需了解创建对象的类,只需要知道需要传递的接口、方法和参数,简化了客户端的实现
- 轻松地在工厂中添加其他类来创建其他类型的对象,而这无需更改客户端代码
- 工厂可以重用现有对象。但是客户端创建对象总是创建一个新的对象
Factory 设计模式有三种变体:
- 简单工厂模式:允许接口创建对象,但不会暴露对象的创建逻辑
- 工厂方法模式:允许接口创建对象,但使用哪个类来创建对象,则交由子类决定
- 抽象工厂模式:抽象工厂是一个能够创建一系列相关对象而无需指定/公开其具体类的接口。该模式能够提供其他工厂的对象,在其内部创建其他对象
2. 简单工厂模式
工厂可以帮助开发人员创建不同类型的对象,而不是直接将对象实例化
from abc import ABCMeta, abstractmethod
class Animal(metaclass=ABCMeta):
@abstractmethod
def do_say(self):
pass
class Dog(Animal):
def do_say(self):
print("Wong Wong!!")
class Cat(Animal):
def do_say(self):
print("Miao Miao!!")
## forest factory defined
class ForestFactory(object):
def make_sound(self, object_type):
return eval(object_type)().do_say()
## client code
if __name__ == '__main__':
ff = ForestFactory()
animal = input("Which animal should make_sound Dog or Cat?")
ff.make_sound(animal)
运行结果:
Which animal should make_sound Dog or Cat?Cat
Miao Miao!!
3. 工厂方法模式
工厂方法模式要点:
- 我们定义了一个接口来创建,但是工厂本身并不负责创建对象,而是将这个任务交由子类来完成,即子类决定了要实例化哪些类
- 工厂方法的创建是通过继承而不是通过实例化来完成的
- 工厂方法使设计更加具有可定制性。它可以返回相同的实例或子类,而不是某些类型的对象(就像在简单工厂方法中的那样)
实现工厂方法:
假设我们想在不同类型的社交网站(例如:LinkedIn、Facebook)上为个人或公司建立简介
那么每个简介都有某些特定的组成章节
LinkedIn: 个人专利或出版作品区 + 个人信息区
Facebook: 在相册中度假的照片区 + 个人信息区
简言之,我们通过将正确的区添加到相应的简介中来创建不同类型的简介
from abc import ABCMeta, abstractmethod
class Section(metaclass=ABCMeta):
@abstractmethod
def describe(self):
pass
class PersonalSection(Section):
def describe(self):
print("个人信息 Section")
class AlbumSection(Section):
def describe(self):
print("相册 Section")
class PatentSection(Section):
def describe(self):
print("专利区 Section")
class PublicationSection(Section):
def describe(self):
print("出版作品 Section")
我们创建一个Profile抽象类,提供一个工厂方法createProfile(),创建相应的简介
工厂方法类:只提供了接口,并没有实际创建类
子类决定使用哪些区
class Profile(metaclass=ABCMeta):
def __init__(self):
self.sections = []
self.createProfile()
@abstractmethod
def createProfile(self):
pass
def getSections(self):
return self.sections
def addSections(self, section):
self.sections.append(section)
class linkedin(Profile):
def createProfile(self):
self.addSections(PersonalSection())
self.addSections(PatentSection())
self.addSections(PublicationSection())
class facebook(Profile):
def createProfile(self):
self.addSections(PersonalSection())
self.addSections(AlbumSection())
if __name__ == "__main__":
profile_type = input("Which Profile you'd like to create? \
[LinkedIn or FaceBook]")
profile = eval(profile_type.lower())()
print("Creating Profile..", type(profile).__name__)
print("Profile has sections --", profile.getSections())
运行结果:
Which Profile you’d like to create? [LinkedIn or FaceBook]facebook
Creating Profile… facebook
Profile has sections – [<main.PersonalSection object at 0x7fb4c03bff28>,
<main.AlbumSection object at 0x7fb4c03bf780>]
可以发现:工厂方法里面的工厂不创建类,只提供接口
facebook选定后,facebook类就被实例化,Profile中的__init__方法被调用,
–> __init__中的createProfile()方法被调用 --> addSections被调用,
–> Section列表的append()方法被调用
4. 工厂方法模式的优点
- ta具有更大的灵活性,代码更加通用,因为它不是单纯地实例化某个类。这样实现哪些类取决于接口
- 松耦合,因为创建对象的代码与使用它的代码是分开的。客户端无需关心传递哪些参数以及需要实例化哪些类。由于添加新类更加容易,所以降低了维护成本
5. 抽象工厂模式
抽象工厂模式的主要目的是提供一个接口来创建一系列相关对象,而无需指定具体的类
抽象工厂的目标是创建一系列相关对象,而工厂方法模式将创建实例的任务交给了子类
实际上,抽象工厂模式不仅确保客户端与对象的创建相互隔离,同时还确保客户端能够使用创建的对象
但是客户端只能通过接口访问对象
from abc import ABCMeta, abstractmethod
# 抽象工厂基类:pizza factory
class PizzaFactory(metaclass=ABCMeta):
@abstractmethod
def createVegPizza(self):
pass
@abstractmethod
def createNonVegPizza(self):
pass
# Indian pizza factory
class IndianPizzaFactory(PizzaFactory):
def createVegPizza(self):
return DeluxVeggiePizza()
def createNonVegPizza(self):
return ChickenPizza()
# USA pizza factory
class USPizzaFactory(PizzaFactory):
def createVegPizza(self):
return MexicanVegPizza()
def createNonVegPizza(self):
return HamPizza()
class VegPizza(metaclass=ABCMeta):
@abstractmethod
def prepare(self, VegPizza):
pass
class NonVegPizza(metaclass=ABCMeta):
@abstractmethod
def serve(self, VegPizza):
pass
class DeluxVeggiePizza(VegPizza):
def prepare(self):
print("Prepare ", type(self).__name__)
class ChickenPizza(NonVegPizza):
def serve(self, VegPizza):
print(type(self).__name__, "is served with Chicken on ", \
type(VegPizza).__name__)
class MexicanVegPizza(VegPizza):
def prepare(self):
print("Prepare ", type(self).__name__)
class HamPizza(NonVegPizza):
def serve(self, VegPizza):
print(type(self).__name__, "is served with Ham on ", \
type(VegPizza).__name__)
class PizzaStore:
def __init__(self):
pass
def makePizza(self):
for factory in [IndianPizzaFactory(), USPizzaFactory()]:
self.factory = factory
self.NonVegPizza = self.factory.createNonVegPizza()
self.VegPizza = self.factory.createVegPizza()
self.VegPizza.prepare()
self.NonVegPizza.serve(self.VegPizza)
pizza = PizzaStore()
pizza.makePizza()
运行结果:
Prepare DeluxVeggiePizza
ChickenPizza is served with Chicken on DeluxVeggiePizza
Prepare MexicanVegPizza
HamPizza is served with Ham on MexicanVegPizza
解析:
- 与工厂方法的主要区别是: 将工厂多了一层抽象,它创建的不是一个对象而是一类对象
- 本例中,当创建工厂后,会同步创建蔬菜披萨和非蔬菜披萨两个类
6. 工厂方法与抽象工厂方法的区别
工厂方法 | 抽象工厂方法 |
---|---|
它向客户端开放了一个创建对象的方法 | 抽象工厂包含了一个或多个工厂方法来创建一系列的相关对象 |
它使用继承和子类来决定要创建哪个对象 | 它使用组合将创建对象的任务委托给其他类 |
工厂方法用于创建一个产品 | 抽象工厂用于创建相关产品系列 |
7. 小结
1、介绍了工厂设计模式及其使用的上下文; 如何在软件架构有效使用工厂模式
2、简单工厂模式:可以运行时根据客户端传入的参数类型来创建相应的实例
3、工厂方法模式:定义一个接口创建对象,但是对象的实例化交给子类
4、抽象工厂模式:提供接口,无需指定具体的类就能创建一系列的相关对象
5、Python实现3种工厂模式,并比较了工厂方法与抽象工厂方法的区别