精通Python设计模式第二版 第 2 章 建造者模式 学习笔记
假设我们要创建一个由对各部分组成的独享,而且创建过程需要一步一步地完成。只有创建了所有的部分,该对象才是完成的。这种需求可以使用建造者模式帮助我们。建造者模式将复杂对象的构建与其表示分离。通过将构建与表示分离,相同的构建可用于创建几个不同的表示
假设我们想创建一个HTML页面生成器。HTML页面的基础结构(构建部分)大同小异:以开始,以结束,在HTML部分中有
元素,在head部分中有和 原色,以此类推。但是页面的表示可能不同。每个页面都有自己的标题、头部和不同的内容。此外,页面通常按步骤构建:一个函数添加标题,一个函数添加主头部,一个函数添加脚注,等等。只有在页面的整个结构完成之后,才能使用一个最终的渲染函数将其呈现给客户端。我们可以进一步拓展HTML生成器,使其能够生成完全不同的HTML页面。一个页面可能包含表格,一个可能包含图像库,一个页面可能包含联系人表单,等等
假设我们想买一台新计算机,计算机的配置我们自己挑选。我们可以使用建造者模式解决这个问题,在这个方案里,我们就是指挥者,向制造商(builder)发出指令
class Computer():
"""
Computer :
Attributes:
"""
def __init__(self, serial_number):
self.serial = serial_number
self.memory = None # 单位为GB
self.hdd = None # 单位为GB
self.gpu = None
def __str__(self):
info = (f'Memory: {self.memory}GB',
f'Hard Disk: {self.hdd}GB',
f'Graphics Card: {self.gpu}')
return '\n'.join(info)
class Computer():
"""
Computer :
Attributes:
"""
def __init__(self, serial_number):
self.serial = serial_number
self.memory = None # 单位为GB
self.hdd = None # 单位为GB
self.gpu = None
def __str__(self):
info = (f'Memory: {self.memory}GB',
f'Hard Disk: {self.hdd}GB',
f'Graphics Card: {self.gpu}')
return '\n'.join(info)
class HardwareEngineer():
"""
HardwareEngineer :
Attributes:
"""
def __init__(self):
self.builder = None
def construct_computer(self, memory, hdd, gpu):
self.builder = ComputerBuilder()
(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()
获得打印如下
Memory: 8GB
Hard Disk: 500GB
Graphics Card: GeForce GTX 650 Ti
class Computer():
"""
Computer :
Attributes:
"""
def __init__(self, serial_number):
self.serial = serial_number
self.memory = None # 单位为GB
self.hdd = None # 单位为GB
self.gpu = None
def __str__(self):
info = (f'Memory: {self.memory}GB',
f'Hard Disk: {self.hdd}GB',
f'Graphics Card: {self.gpu}')
return '\n'.join(info)
class ComputerBuilder():
"""
ComputerBuilder :
Attributes:
"""
def __init__(self):
self.computer = Computer('AG23385193')
def configure_memory(self, amount):
self.computer.memory = amount
def configure_hdd(self, amount):
self.computer.hdd = amount
def configure_gpu(self, gpu_model):
self.computer.gpu = gpu_model
class HardwareEngineer():
"""
HardwareEngineer :
Attributes:
"""
def __init__(self):
self.builder = None
def construct_computer(self, memory, hdd, gpu):
self.builder = ComputerBuilder()
(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()
这个方案中,我们引入了建造者CompuerBuilder、指挥者HardwareEngineer,并逐步构建了一台计算机,它现在支持不同的配置memory,hdd,gpu
现在让我们看看如果使用建造者设计模式来制作比萨订购程序。披萨的例子特别又去,因为披萨的制作不走应该遵循特定的顺序。加调味汁之前,首先要准备面团。加配料之前,首先要加调味汁。只有调味汁和配料都放在面团上后,你才能开始烤披萨。此外,每个披萨通常需要不同的烘烤时间,这取决于面团的厚度和所用的配料
使用这些枚举来记录制作披萨的状态以及披萨的一些相关属性
from enum import Enum
import time
PizzaProgress = Enum('PizzaProgress', 'queued preparation baking ready')
PizzaDough = Enum('PizzaDough', 'thin thick')
PizzaSauce = Enum('PizzaSauce', 'tomato creme_fraiche')
PizzaTopping = Enum('PizzaTopping', 'mozzarella double_mozzarella bacon ham mushrooms red_onion oregano')
STEP_DELAY = 3 # 单位为秒
class Pizza():
"""
Pizza :
Attributes:
"""
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(f'preparing the {self.dough.name} dough of your {self}...')
time.sleep(STEP_DELAY)
print(f'done with the {self.dough.name} dough')
有两个建造者:一个用于创建玛格丽特披萨,另一个用于创建奶油培根披萨。每个建造者创建一个Pizza实例,并包含遵循比萨制作过程的方法:prepare_dough()、add_sauce() 等。
class MargaritaBuilder():
"""
MargaritaBuilder :
Attributes:
"""
def __init__(self):
self.pizza = Pizza('margarita')
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):
topping_desc = 'double mozzarella, oregano'
topping_items = (PizzaTopping.double_mozzarella, PizzaTopping.oregano)
print(f'adding the topping ({topping_desc}) to your margarita')
self.pizza.topping.append([t for t in topping_items])
time.sleep(STEP_DELAY)
print(f'done with the topping ({topping_desc})')
def bake(self):
self.progress = PizzaProgress.baking
print(f'baking your margarita for {self.baking_time} seconds')
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
print('your margarita is ready')
class CreamyBaconBuilder():
"""
CreamyBaconBuilder :
Attributes:
"""
def __int__(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 creme fraiche sauce to your creamy bacon')
self.pizza.sauce = PizzaSauce.creme_fraiche
time.sleep(STEP_DELAY)
print('done with the creme fraiche sauce')
def add_topping(self):
topping_desc = 'mozzarella, bacon, ham, mushrooms, red onion, oregano'
topping_items = (PizzaTopping.mozzarella,
PizzaTopping.bacon,
PizzaTopping.ham,
PizzaTopping.mushrooms,
PizzaTopping.red_onion,
PizzaTopping.oregano)
print(f'adding the topping ({topping_desc}) to your creamy bacon')
self.pizza.topping.append([t for t in topping_items])
time.sleep(STEP_DELAY)
print(f'done with the topping ({topping_desc})')
def bake(self):
self.progress = PizzaProgress.baking
print(f'baking your creamy bacon for {self.baking_time} seconds')
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
print('your creamy bacon is ready')
在这个例子中 指挥者是服务员。Waiter类的核心是construct_pizza()方法,它接收一个建造者作为参数,并以正确的顺序执行所有比萨准备。
class Waiter():
"""
Waiter :
Attributes:
"""
def __init__(self):
self.builder = None
def construct_pizza(self, builder):
self.builder = builder
steps = (builder.prepare_dough,
builder.add_sauce,
builder.add_topping,
builder.bake)
[step() for step in steps]
@property
def pizza(self):
return self.builder.pizza
validate_style() 确保用户提供有效的输入
def validate_style(builders):
try:
input_msg = 'What pizza would you like, [m]argarita or [c]reamy bacon? \n'
pizza_style = input(input_msg)
builder = builders[pizza_style]()
valid_input = True
except KeyError:
error_msg = 'Sorry, only margarita (key m) and creamy bacon (key c) are available'
print(error_msg)
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(f'Enjoy your {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 mozzarella, oregano)
baking your margarita for 5 seconds
your margarita is ready
Enjoy your margarita!
from enum import Enum
import time
PizzaProgress = Enum('PizzaProgress', 'queued preparation baking ready')
PizzaDough = Enum('PizzaDough', 'thin thick')
PizzaSauce = Enum('PizzaSauce', 'tomato creme_fraiche')
PizzaTopping = Enum('PizzaTopping', 'mozzarella double_mozzarella bacon ham mushrooms red_onion oregano')
STEP_DELAY = 3 # 单位为秒
class Pizza():
"""
Pizza :
Attributes:
"""
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(f'preparing the {self.dough.name} dough of your {self}...')
time.sleep(STEP_DELAY)
print(f'done with the {self.dough.name} dough')
class MargaritaBuilder():
"""
MargaritaBuilder :
Attributes:
"""
def __init__(self):
self.pizza = Pizza('margarita')
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):
topping_desc = 'double mozzarella, oregano'
topping_items = (PizzaTopping.double_mozzarella, PizzaTopping.oregano)
print(f'adding the topping ({topping_desc}) to your margarita')
self.pizza.topping.append([t for t in topping_items])
time.sleep(STEP_DELAY)
print(f'done with the topping ({topping_desc})')
def bake(self):
self.progress = PizzaProgress.baking
print(f'baking your margarita for {self.baking_time} seconds')
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
print('your margarita is ready')
class CreamyBaconBuilder():
"""
CreamyBaconBuilder :
Attributes:
"""
def __int__(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 creme fraiche sauce to your creamy bacon')
self.pizza.sauce = PizzaSauce.creme_fraiche
time.sleep(STEP_DELAY)
print('done with the creme fraiche sauce')
def add_topping(self):
topping_desc = 'mozzarella, bacon, ham, mushrooms, red onion, oregano'
topping_items = (PizzaTopping.mozzarella,
PizzaTopping.bacon,
PizzaTopping.ham,
PizzaTopping.mushrooms,
PizzaTopping.red_onion,
PizzaTopping.oregano)
print(f'adding the topping ({topping_desc}) to your creamy bacon')
self.pizza.topping.append([t for t in topping_items])
time.sleep(STEP_DELAY)
print(f'done with the topping ({topping_desc})')
def bake(self):
self.progress = PizzaProgress.baking
print(f'baking your creamy bacon for {self.baking_time} seconds')
time.sleep(self.baking_time)
self.progress = PizzaProgress.ready
print('your creamy bacon is ready')
class Waiter():
"""
Waiter :
Attributes:
"""
def __init__(self):
self.builder = None
def construct_pizza(self, builder):
self.builder = builder
steps = (builder.prepare_dough,
builder.add_sauce,
builder.add_topping,
builder.bake)
[step() for step in steps]
@property
def pizza(self):
return self.builder.pizza
def validate_style(builders):
try:
input_msg = 'What pizza would you like, [m]argarita or [c]reamy bacon? \n'
pizza_style = input(input_msg)
builder = builders[pizza_style]()
valid_input = True
except KeyError:
error_msg = 'Sorry, only margarita (key m) and creamy bacon (key c) are available'
print(error_msg)
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(f'Enjoy your {pizza}!')
if __name__ == '__main__':
main()
简略版建造者模型 解决披萨订单问题
class Pizza():
"""
Pizza :
Attributes:
"""
def __init__(self,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 = (f'Garlic: {garlic}',f'Extra cheese: {cheese}')
return '\n'.join(info)
class PizzaBuilder():
"""
PizzaBuilder :
Attributes:
"""
def __init__(self):
self.extra_cheese = False
self.garlic = False
def add_garlic(self):
self.garlic = True
return self
def add_cheese(self):
self.extra_cheese = True
return self
def build(self):
return Pizza(self)
if __name__ == '__main__':
pizza = Pizza.PizzaBuilder().add_cheese().add_garlic().build()
print(pizza)
获得打印如下
Garlic: yes
Extra cheese: yes