建造者模式: 提供了一种精细化控制对象创建过程的模型。假设一个对象必须经过多步操作才能创建成功,我们就必须使用建造者模式。以产生 HTML 页面为例:要产生一个 HTML 页面,我们必须逐步设置:页面标题、文本标题、内容主体和页脚。如下图所示:
在整个 建造者模式 中,涉及到三个角色:事务对象、建造者、指挥者,我们的最终目的就是创建 事务对象实例;
三者在实现过程中的类关系如下:
建造者模式 主要用于以下场景:
建造者模式 与 工厂模式 都是用来创建对象,但是它们的侧重点不一样;
下面以生产笔记本为例来说明两者之间的区别,笔记本的参数为:
AG23385193
;GeForce GTX 650 Ti
# coding: utf-8
#电脑
class Computer:
def __init__(self, serial_number):
self.serial = serial_number
self.memory = None # 单位为GB
self.hdd = None # 单位为GB
self.gpu = None
def __str__(self):
info = ('Memory: {}GB'.format(self.memory),
'Hard Disk: {}GB'.format(self.hdd),
'Graphics Card: {}'.format(self.gpu))
return '\n'.join(info)
# 建造者
class ComputerBuilder:
def __init__(self):
self.computer = Computer('AG23385193') # 设置序列号
# 配置内存
def configure_memory(self, amount):
self.computer.memory = amount
# 配置hdd
def configure_hdd(self, amount):
self.computer.hdd = amount
# 配置gpu
def configure_gpu(self, gpu_model):
self.computer.gpu = gpu_model
# 指挥者
class HardwareEngineer:
def __init__(self):
self.builder = None
# 在建造的时候,才创建建造者对象
def construct_computer(self, memory, hdd, gpu):
self.builder = ComputerBuilder()
[step for step in (self.builder.configure_memory(memory),
self.builder.configure_hdd(hdd),
self.builder.configure_gpu(gpu))]
@property
def computer(self):
return self.builder.computer
def main():
# 申请一个指挥者
engineer = HardwareEngineer()
# 指挥者配置电脑
engineer.construct_computer(hdd=500, memory=8, gpu='GeForce GTX 650 Ti')
# 返回实例对象
computer = engineer.computer #得到一个电脑实例
print(computer)
if __name__ == '__main__':
main()
# coding: utf-8
MINI14 = '1.4GHz Mac mini'
# 工厂模式产生类实例
class AppleFactory:
# 防止实例化
class MacMini14:
def __init__(self):
self.memory = 4 # 单位为GB
self.hdd = 500 # 单位为GB
self.gpu = 'Intel HD Graphics 5000'
def __str__(self):
info = ('Model: {}'.format(MINI14),
'Memory: {}GB'.format(self.memory),
'Hard Disk: {}GB'.format(self.hdd),
'Graphics Card: {}'.format(self.gpu))
return '\n'.join(info)
# 工厂函数
def build_computer(self, model):
if (model == MINI14):
return self.MacMini14()
else:
print("I dont't know how to build {}".format(model))
if __name__ == '__main__':
afac = AppleFactory()
mac_mini = afac.build_computer(MINI14)
print(mac_mini)
注意: 这里嵌套了 MacMini14类。这是禁止类直接实例化的一种方式。
上述两种方式呈现的效果是一样的。
我们知道制造 Pizza 是要遵循一定的步骤的:选择面团,选择配料,装饰配料,烘焙,等待,开吃等步骤。现在我们要根据用户的需求制造两种 Pizza,一种是玛格丽特 Pizza,一种是奶油 Pizza。两种 Pizza 的实现步骤是一样的,但是具体的实现细节却不一样。所以,有如下结论:
from enum import Enum
import time
# Pizza的过程: [队列,预备,烘焙,准备]
PizzaProgress = Enum('PizzaProgress', 'queued preparation baking ready')
# Pizza的面团
PizzaDough = Enum('PizzaDough', 'thin thick')
# Pizza的配料
PizzaSauce = Enum('PizzaSauce', 'tomato creme_fraiche')
# Pizza的装饰
PizzaTopping = Enum('PizzaTopping', 'mozzarella double_mozzarella bacon ham mushrooms red_onion oregano')
STEP_DELAY = 3 # 考虑是示例,单位为秒
STEP_DELAY = 3
用于模拟每一个步骤所花费的时间最终的产品是一个 Pizza,由 Pizza 类来描述,不支持直接实例化,该类的注意职责就是数据初始化为默认值。
唯一的例外是方法 prepare_dough()
,将方法 prepare_dough()
方法定义在 Pizza 类而不是 建造者 中,主要考虑到:
# 产品是Pizza
class Pizza:
def __init__(self, name):
self.name = name
self.dough = None # 生面团
self.sauce = None # 配料
self.topping = [] # 在糕点上装饰配料
def __str__(self):
return self.name
# 模拟准备生面团的过程
def prepare_dough(self, dough):
self.dough = dough # 模拟准备面团
print('preparing the {} dough of your {}...'.format(self.dough.name, self))
time.sleep(STEP_DELAY)
print('done with the {} dough'.format(self.dough.name)) # 面团准备好了
在应用中有两个建造者:一个是玛格丽特建造者(MargaritaBuilder)、另一个是奶油熏肉建造者(CreamyBaconBuilder),包含的主要步骤为:
prepare_dough():
准备面团;add_sauce():
准备配料;add_topping():
添加点缀;bake():
烘焙;每个建造者 实现的细节是不一样的,例如:例如,玛格丽特比萨的配料是双层马苏里拉奶酪( mozzarella)和 牛至( oregano),而奶油熏肉比萨的配料是马苏里拉奶酪( mozzarella)、熏肉( bacon)、火腿( ham)、蘑菇( mushrooms)、紫洋葱( red onion)和牛至( oregano)等。具体代码实现如下:
# 两个建造者类的接口函数都是相同的
# 玛格丽特比萨建造者
class MargaritaBuilder:
def __init__(self):
self.pizza = Pizza('margarita') # 建造玛格丽特Pizza
self.progress = PizzaProgress.queued # 记录制作的步骤
self.baking_time = 5 # 烘焙时间
# 准备面团
def prepare_dough(self):
self.progress = PizzaProgress.preparation
self.pizza.prepare_dough(PizzaDough.thin)
# 准备配料
def add_sauce(self):
print('adding the tomato sauce to your margarita...')
self.pizza.sauce = PizzaSauce.tomato
time.sleep(STEP_DELAY)
print('done with the tomato sauce')
# 添加配料的过程
def add_topping(self):
print('adding the topping (double mozzarella, oregano) to your margarita')
self.pizza.topping.append([i for i in
(PizzaTopping.double_mozzarella, PizzaTopping.oregano)])
time.sleep(STEP_DELAY)
print('done with the topping (double mozzarrella, oregano)')
# 烘焙的过程
def bake(self):
self.progress = PizzaProgress.baking
print('baking your margarita for {} seconds'.format(self.baking_time))
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
print('your margarita is ready')
# 奶油披萨建造者
class CreamyBaconBuilder:
def __init__(self):
self.pizza = Pizza('creamy bacon') # 奶油熏肉披萨
self.progress = PizzaProgress.queued
self.baking_time = 7 # 烘焙时间
# 准备面团的过程
def prepare_dough(self):
self.progress = PizzaProgress.preparation
self.pizza.prepare_dough(PizzaDough.thick)
# 准备配料过程
def add_sauce(self):
print('adding the crème fraîche sauce to your creamy bacon')
self.pizza.sauce = PizzaSauce.creme_fraiche
time.sleep(STEP_DELAY)
print('done with the crème fraîche sauce')
# 添加配料过程
def add_topping(self):
print('adding the topping (mozzarella, bacon, ham, mushrooms, red onion, oregano) to your creamy bacon')
self.pizza.topping.append([t for t in
(PizzaTopping.mozzarella, PizzaTopping.bacon,
PizzaTopping.ham, PizzaTopping.mushrooms,
PizzaTopping.red_onion, PizzaTopping.oregano)])
time.sleep(STEP_DELAY)
print('done with the topping (mozzarella, bacon, ham, mushrooms, red onion, oregano)')
# 烘焙过程
def bake(self):
self.progress = PizzaProgress.baking
print('baking your creamy bacon for {} seconds'.format(self.baking_time))
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
print('your creamy bacon is ready')
指挥者 用于按照一定的步骤调用 建造者 创建 Pizza,在这个例子中,指挥者 就是服务员(Waiter)。 Waiter类 的核心是construct_pizza
方法,该方法接受一个 建造者 作为参数,并以正确的顺序执行比萨的所有准备步骤。
选择恰当的建造者(可以在运行时选择),无需修改指挥者( Waiter)的任何代码,就能制作不同的比萨。
Waiter类 还包含 pizza()
方法,会向调用者返回最终产品(准备好的 Pizza)。
具体代码如下:
# 指挥者:服务员
class Waiter:
def __init__(self):
self.builder = None
# 构建比萨
def construct_pizza(self, builder):
"""把制造者实例作为参数传递进来"""
self.builder = builder # 在建造的时候创建builder对象
[step() for step in (builder.prepare_dough,
builder.add_sauce, builder.add_topping, builder.bake)]
# 打印比萨的信息
@property
def pizza(self):
return self.builder.pizza
在下述代码中,还添加了一个输入检查的功能,以确保用户提供有效的输入。当前案例中这个输入是映射到一个比萨建造者的字符;- - 输入字符 m
表示使用 MargaritaBuilder类
;
c
则使用 CreamyBaconBuilder类
。这些映射关系存储在参数 builder
中。该函数会返回一个元组,如果输入有效,则元组的第一个元素被设置为 True
, 否则为False
,如下所示
# 确保用户的输入是有效的
def validate_style(builders):
"""
builders:建造者类字典
"""
try:
pizza_style = input('What pizza would you like, [m]argarita or [c]reamy bacon? ')
# 根据用户选择创建 建造者实例
builder = builders[pizza_style]()
valid_input = True
except KeyError as err:
print('Sorry, only margarita (key m) and creamy bacon (key c) are available')
return (False, None)
return (True, builder)
def main():
builders = dict(m=MargaritaBuilder, c=CreamyBaconBuilder)
valid_input = False
while not valid_input:
# 根据用户选择,创建建造者实例
valid_input, builder = validate_style(builders)
print()
# 实例化比萨指挥者
waiter = Waiter()
waiter.construct_pizza(builder)
pizza = waiter.pizza
print()
print('Enjoy your {}!'.format(pizza))
if __name__ == '__main__':
main()
运行结果如下:
结果:
What pizza would you like, [m]argarita or [c]reamy bacon? m
preparing the thin dough of your margarita...
done with the thin dough
adding the tomato sauce to your margarita...
done with the tomato sauce
adding the topping (double mozzarella, oregano) to your margarita
done with the topping (double mozzarrella, oregano)
baking your margarita for 5 seconds
your margarita is ready
Enjoy your margarita!
在上述实现的过程中,我们把两个建造者类存放在一个字典中,用户可以选择创建哪一种建造者实例,然后指挥者创调用该建造者实例创建相应的 Pizza。
class Pizza:
def __init__(self, builder):
"""
Params:
- builder:建造者实例
"""
self.garlic = builder.garlic
self.extra_cheese = builder.extra_cheese
def __str__(self):
garlic = 'yes' if self.garlic else 'no'
cheese = 'yes' if self.extra_cheese else 'no'
info = ('Garlic: {}'.format(garlic), 'Extra cheese: {}'.format(cheese))
return '\n'.join(info)
# 建造者
class PizzaBuilder:
def __init__(self):
self.extra_cheese = False
self.garlic = False
# 添加大蒜
def add_garlic(self):
self.garlic = True
return self # 返回的是实例对象
# 添加芝士
def add_extra_cheese(self):
self.extra_cheese = True
return self # 返回的是实例对象
# 建造
def build(self):
return Pizza(self) # self是builder实例对象
if __name__ == '__main__':
pizza = Pizza.PizzaBuilder().add_garlic().add_extra_cheese().build()
print(pizza)
我们来仔细分析以下上述代码:
Pizza.PizzaBuilder()
:将创建一个 建造者 的实例对象;Pizza.PizzaBuilder().add_garlic()
:等效于创建一个建造者,建造者 执行添加大蒜操作,返回的是 建造者 实例对象;Pizza.PizzaBuilder().add_garlic().add_extra_cheese()
:等效于创建一个建造者,建造者 依次执行添加大蒜操作、添加芝士操作,返回的是 建造者 实例对象;Pizza.PizzaBuilder().add_garlic().add_extra_cheese().build()
:等效于创建一个建造者,建造者 依次执行添加大蒜操作、添加芝士操作,返回的是 Pizza 实例对象;源码在这里