建造者模式(Builder Pattern)构造模式: 控制复杂对象的构造过程
当对象需要多个部分组合起来一步步创建,并且创建和表示分离的时候。可以这么理解,你要买电脑,工厂模式直接返回一个你需要型号的电脑,但是构造模式允许你自定义电脑各种配置类型,组装完成后给你。这个过程你可以传入builder从而自定义创建的方式。
1 介绍
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
主要解决:主要解决在软件系统中,有时候面临着"一个复杂对象"的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。
关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。 2、JAVA 中的 StringBuilder。
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。
注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
2 适用场景
- 对象构建步骤很复杂,且需要根据不同的步骤组合不同的产品
当对象构建步骤很复杂,需要很多步骤,且我们需要根据这些不同的步骤组合成不同的产品类型时
如果我们知道一个对象必须经过多个步骤来创建并且要求同一个构造过程可以产生不同的表现,就可以使用建造者模式。这种需求存在于许多应用中,例如页面生成器(本章提到的HTML 页面生成器之类)、文档转换器以及用户界面(User Interface, UI)表单创建工具。
该模式中,有两个参与者:
建造者(builder) 和指挥者(director),建造者负责创建复杂对象的各个组成部分,指挥者使用一个建造者实例控制建造的过程。
快餐店使用的就是建造者设计模式。即使存在多种汉堡包(经典款、奶酪汉堡包等)和不同包装(小盒子、中等大小盒子等),准备一个汉堡包及打包(盒子或纸袋)的流程都是相同的。经典款汉堡包和奶酪汉堡包之间的区别在于表现,而不是建造过程。指挥者是出纳员,将需要准备什么餐品的指令传达给工作人员,建造者是工作人员中的个体,关注具体的顺序。
3 实现步骤
步骤一:创建对应的产品抽象类/产品类
步骤二:创建产品的指挥者类,即最终提供给客户端的产品的实例对象,以及组装过程
步骤三:创建构建者抽象类,主要是定义构建者通用属性/方法,以及继承者必须实现的功能抽象
步骤四:具体构建者类实现
import abc
#步骤一:创建对应的产品抽象类/产品类
class Building(object):
def __init__(self):
self.floor = None
self.size = None
def __repr__(self):
return 'Floor: {0.floor} | size: {0.size}'.format(self)
#步骤二:创建产品的指挥者类,即最终提供给客户端的产品的实例对象,以及组装过程
class Director(object):
def __init__(self):
self.builder = None
def construct_building(self):
"""
#建造者模式下,仅在需要时客户端代码才显式地请求指挥者返回最终的对象
"""
self.builder.new_building()
self.builder.build_floor()
self.builder.build_size()
def get_building(self):
return self.builder.building
#步骤三:创建构建者抽象类,主要是定义构建者通用属性/方法,以及继承者必须实现的功能抽象
#Abstract builder
class AbsBuilder(object):
def __init__(self):
self.building = None
def new_building(self):
self.building = Building()
@abc.abstractmethod
def build_floor(self):
pass
@abc.abstractmethod
def build_size(self):
pass
#步骤四:具体构建者类实现
class HouseBuilder(AbsBuilder):
def build_floor(self):
self.building.floor = 'one'
def build_size(self):
self.building.size = '220 squre'
class FlatBuilder(AbsBuilder):
def build_floor(self):
self.building.floor = 'seven'
def build_size(self):
self.building.size = '140 squre'
class Client(object):
def build(self, build_type):
if build_type == "House":
director = Director()
builder = HouseBuilder()
director.builder = builder
director.construct_building()
building = director.get_building()
print(building)
else:
director = Director()
builder = FlatBuilder()
director.builder = builder
director.construct_building()
building = director.get_building()
print(building)
if __name__ == "__main__":
build_type = "Flat"
client = Client()
client.build(build_type)
4 代码实践
案例一:烤制Pizza
使用建造者设计模式实现一个比萨订购的应用。
比萨的例子特别有意思,因为准备好一个比萨需经过多步操作,且这些操作要遵从特定顺序。
要添加调味料,你得先准备生面团。要添加配料,你得先添加调味料。
并且只有当生面团上放了调味料和配料之后才能开始烤比萨。
此外,每个比萨通常要求的烘培时间都不一样,依赖于生面团的厚度和使用的配料。
import time
from abc import abstractmethod, ABCMeta
from enum import Enum
PizzaProcess = 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 = 1
"""
步骤1: 定义产品
"""
class Pizza(object):
"""
使用建造者模式,则最终产品(类)并没有多少职责,因为它不支持直接实例化。建造者会创建一个最终产品的实例
"""
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))
"""
步骤2:定义建造者抽象类,约定构造过程
"""
class AbsBuilder(metaclass=ABCMeta):
# builder默认配置
def __init__(self):
self.pizza = None
self.process = PizzaProcess.queued
self.baking_time = 5
@abstractmethod
def prepare_dough(self):
pass
@abstractmethod
def add_sauce(self):
pass
@abstractmethod
def add_topping(self):
pass
@abstractmethod
def bake(self):
pass
"""
步骤3:建造者具体代码实现
"""
class MargaritaBudiler(AbsBuilder):
def __init__(self):
super(MargaritaBudiler, self).__init__()
self.pizza = Pizza("Margarita")
self.pizza.name = "Margarita"
def prepare_dough(self):
self.process = PizzaProcess.preparation
self.pizza.prepare_dough(PizzaDough.thin)
def add_sauce(self):
print("开始添加番茄酱")
self.pizza.sauce = PizzaSauce.tomato
print("番茄酱完成")
def add_topping(self):
print("开始添加蔬菜/熏肉等配菜")
self.pizza.topping.append([item for item in (PizzaTopping.double_mozzarella, PizzaTopping.oregano)])
time.sleep(STEP_DELAY)
print("配菜加好了")
def bake(self):
print("开始烤Pizza")
self.process = PizzaProcess.baking
time.sleep(self.baking_time)
self.process = PizzaProcess.ready
print("Pizza烤好了")
class CreamyBaconBuilder(AbsBuilder):
def __init__(self):
super(CreamyBaconBuilder, self).__init__()
self.pizza = Pizza("CreamyBacon")
self.pizza.name = "CreamyBacon"
self.baking_time = 5
def prepare_dough(self):
self.process = PizzaProcess.preparation
self.pizza.prepare_dough(PizzaDough.thin)
def add_sauce(self):
print("开始添加creme_fraiche酱")
self.pizza.sauce = PizzaSauce.creme_fraiche
print("creme_fraiche酱完成")
def add_topping(self):
print("开始添加蔬菜/熏肉等配菜")
self.pizza.topping.append([item for item in (PizzaTopping.bacon, PizzaTopping.mushrooms)])
time.sleep(STEP_DELAY)
print("配菜加好了")
def bake(self):
print("开始烤Pizza")
self.process = PizzaProcess.baking
time.sleep(self.baking_time)
print("烤制{}秒".format(self.baking_time))
self.process = PizzaProcess.ready
print("Pizza烤好了")
"""
步骤4: 定义指挥者Waiter类,通过调用builder完成pizza的组装烤制
"""
class Waiter(object):
def __init__(self):
self.builder = None
def construct_pizza(self, builder):
self.builder = builder
[step() for step in (builder.prepare_dough, builder.add_sauce, builder.add_topping, builder.bake)]
@property
def get_pizza(self):
return self.builder.pizza
def validate_style(builders):
"""
输入验证
:param builders: 创建者实例 -》dict类型
:return: 满足条件的builder实例 -》tuple类型(False, builder)
"""
try:
pizza_style = input("pizza type, [m]argarita or [c]reamyBacon")
builder = builders[pizza_style.lower()]()
except KeyError as err:
print("不支持这种pizza")
return (False, None)
return (True, builder)
if __name__ == "__main__":
builders = dict(m=MargaritaBudiler, c=CreamyBaconBuilder)
valid_input = False
while not valid_input:
valid_input, builder = validate_style(builders)
waiter = Waiter()
waiter.construct_pizza(builder)
pizza = waiter.get_pizza
代码输出:
pizza type, [m]argarita or [c]reamyBaconm
preparing the thin dough of your Margarita...
done with the thin dough
开始添加番茄酱
番茄酱完成
开始添加蔬菜/熏肉等配菜
配菜加好了
开始烤Pizza
Pizza烤好了
案例二:汉堡店组装套餐
我们假设一个快餐店的商业案例,其中,一个典型的套餐可以是一个汉堡(Burger)和一杯冷饮(Cold drink)。
汉堡(Burger)可以是素食汉堡(Veg Burger)或鸡肉汉堡(Chicken Burger),它们是包在纸盒中。
冷饮(Cold drink)可以是可口可乐(coke)或百事可乐(pepsi),它们是装在瓶子中。
我们将创建一个表示食物条目(比如汉堡和冷饮)的 Item 接口和实现 Item 接口的实体类,
以及一个表示食物包装的 Packing 接口和实现 Packing 接口的实体类,汉堡是包在纸盒中,冷饮是装在瓶子中。
然后我们创建一个 Meal 类,带有 items 的 list 和一个通过结合 item 来创建不同类型的meal。
MealDirector用来给meal添加不同的项目,返回各种套餐
我们的演示类Customer使用 MealDirector 来搭配一个 Meal。
代码实现:
from abc import ABCMeta, abstractmethod
"""
步骤 1
抽象类,定义准备一项食物的流程结构。
"""
#产品制作过程抽象类
class ItemBuilder(metaclass=ABCMeta):
@abstractmethod
def name(self):
pass
@abstractmethod
def packing(self):
pass
@abstractmethod
def price(self):
pass
#产品包装抽象类
class Packing(metaclass=ABCMeta):
@abstractmethod
def papper(self):
pass
@abstractmethod
def bottle(self):
pass
"""
步骤 2
实现 Packing 实体类,提供两种包装方式的具体实现过程。
"""
#包装实现类
class Wrapper(Packing):
@classmethod
def papper(self):
return "papper"
@classmethod
def bottle(self):
return "Bottle"
"""
步骤 3
实现 ItemBuilder 接口的实体类,该类提供了默认的功能。
"""
class Burger(ItemBuilder):
def name(self):
return "this is burger"
def packing(self):
return Wrapper.papper()
def price(self):
return 0.0
class ColdDrink(ItemBuilder):
def name(self):
return "this is cold drink"
def packing(self):
return Wrapper.bottle()
def price(self):
return 0.0
"""
步骤 4
扩展了 Burger 和 ColdDrink 的实体类
"""
class VegBurger(Burger):
def price(self):
return float(25.1)
def name(self):
return "Veg Burger"
class ChickenBurger(Burger):
def price(self):
return float(50.5)
def name(self):
return "Chicken Burger"
#饮料类
class Coke(ColdDrink):
def name(self):
return "Coke"
def price(self):
return 10.0
class Pepsi(ColdDrink):
def name(self):
return "Pepsi"
def price(self):
return 15.5
"""
步骤 5
创建一个 Meal 类,带有上面定义的 Item 对象。
"""
class Meal(object):
items = []
def add_item(self, item):
Meal.items.append(item)
def get_cost(self):
cost = 0
for item in Meal.items:
cost = cost + item.price()
return cost
def show_items(self):
item_detail = {}
for item in Meal.items:
item_detail["name"] = item.name()
item_detail["price"] = item.price()
item_detail["pack"] = item.packing()
return item_detail
"""
步骤 6
创建一个 MealDirector 类,组装套餐。
"""
class MealDirector(object):
def prepare_veg_meal(self):
self.meal = Meal()
self.meal.add_item(VegBurger())
self.meal.add_item(Coke())
return self.meal
def preapre_chicken_meal(self):
self.meal.add_item(ChickenBurger())
self.meal.add_item(Pepsi())
return self.meal
"""
步骤 6
创建一个 Meal 类,带有上面定义的 Item 对象。
"""
class Customer(object):
def meal_choose(self, name):
if name == "veg":
meal = MealDirector().prepare_veg_meal()
return meal
elif name == "chicken":
meal = MealDirector().preapre_chicken_meal()
return meal
else:
print("not support")
return None
if __name__ == "__main__":
meal_name = "veg"
customer = Customer()
meal = customer.meal_choose(meal_name)
print("meal: {0} \nfood list: {1}\n cost: {2}\n".format(meal_name, meal.show_items(), meal.get_cost()))
代码输出:
meal: veg
food list: {'name': 'Veg Burger', 'price': 25.1, 'pack': 'papper'}
cost: 35.1
5 构造模式和工厂模式
区别如下:
- 在于工厂模式以单个步骤创建对象,而建造者模式以多个步骤创建对象,并且几乎始终会使用一个指挥者
- 在工厂模式下,会立即返回一个创建好的对象;而在建造者模式下,仅在需要时客户端代码才显式地请求指挥者返回最终的对象
举例说明:
以我们购买个人电脑为例,工厂模式,类似与我们直接去商场购买一个由厂家组装好的笔记本电脑;建造者模式,类似与我们去百脑汇根据个人需求特点,要求商家按照我们的需求组装电脑。
工厂模式,我们直接去商场购买一个由厂家组装好的笔记本电脑。代码示例:
import abc
#工厂模式
class AppleCompute(object):
def __init__(self):
self.memory = 4 # 单位为GB
self.hdd = 320 # 单位为GB
self.gpu = 'Intel HD Graphics 5000'
def compute(self):
info = ('Model: {}'.format("Apple Laptop"),
'Memory: {}GB'.format(self.memory),
'Hard Disk: {}GB'.format(self.hdd),
'Graphics Card: {}'.format(self.gpu))
return '\n'.join(info)
class HpCompute(object):
def __init__(self):
self.memory = 8 # 单位为GB
self.hdd = 500 # 单位为GB
self.gpu = 'Intel HD Graphics 5000'
def compute(self):
info = ('Model: {}'.format("Hp Laptop"),
'Memory: {}GB'.format(self.memory),
'Hard Disk: {}GB'.format(self.hdd),
'Graphics Card: {}'.format(self.gpu))
return '\n'.join(info)
class ComputerFactory(object):
@abc.abstractmethod
def build_compute(self):
pass
class AppleComputeFactory(ComputerFactory):
def build_compute(self):
return AppleCompute()
class HpComputeFactory(ComputerFactory):
def build_compute(self):
return HpCompute()
class Client(object):
def get_compute(self, modle):
if modle == "Apple":
return AppleComputeFactory()
elif modle == "Hp":
return HpComputeFactory()
else:
print("model not found")
if __name__ == "__main__":
client = Client()
compute = client.get_compute("Apple").build_compute().compute()
print(compute)
建造者模式,我们是指挥者,要求商家按照我们的需求组装电脑。
示例代码如下:
#建造者模式
import abc
#产品
class Computer(object):
def __init__(self, serial_number):
self.serial = serial_number
self.memory = None # 单位为GB
self.hdd = None # 单位为GB
self.gpu = None
def __str__(self):
info = ('Model: {}'.format(self.serial),
'Memory: {}GB'.format(self.memory),
'Hard Disk: {}GB'.format(self.hdd),
'Graphics Card: {}'.format(self.gpu))
return '\n'.join(info)
# compute的建造者
class ComputerBuilder(object):
def __init__(self):
self.computer = Computer("SN-123456-00000")
def config_memory(self, amount):
self.computer.memory = amount
def config_hdd(self, amount):
self.computer.hdd = amount
def config_gpu(self, gpu_type):
self.computer.gpu = gpu_type
# 指挥者
class Director(object):
def __init__(self):
self.builder = None
def construct_computer(self, memory, hdd, gpu):
self.builder = ComputerBuilder()
self.builder.config_memory(memory)
self.builder.config_hdd(hdd)
self.builder.config_gpu(gpu)
def get_computer(self):
return self.builder.computer
class Client(object):
def run(self, memory=4, hdd=500, gpu="Inter I7 pro"):
director = Director() #仅在需要时才返回对象
director.construct_computer(memory, hdd, gpu)
computer = director.get_computer()
print(computer)
if __name__ == "__main__":
client = Client()
client.run(memory=4, hdd=500, gpu="Inter I7 pro")
运行结果:
Model: SN-123456-00000
Memory: 4GB
Hard Disk: 500GB
Graphics Card: Inter I7 pro
6 软件例子
本章一开始提到的HTML例子,在django-widgy中得到了实际应用。django-widgy是一个 Django的第三方树编辑器扩展,可用作内容管理系统(Content Management System,CMS)。它 包含一个网页构建器,用来创建具有不同布局的HTML页面。
django-query-builder是另一个基于建造者模式的Django第三方扩展库,该扩展库可用于动态 地构建SQL查询。使用它,我们能够控制一个查询的方方面面,并能创建不同种类的查询,从简单的到非常复杂的都可以
参考文献:
https://www.runoob.com/design-pattern/builder-pattern.html
https://www.jianshu.com/p/3e635a0771e0
https://www.cnblogs.com/onepiece-andy/p/python-builder-pattern.html