(转自精通Python设计模式)Python设计模式之创建型模式——2.建造者模式
建造者模式将一个复杂对象的构造过程与其表现分离,这样,同一个构造过程可用于创建多个不同的表现。
我们来看个实际的例子,假设我们想要创建一个HMTL页面生成器,HTML页面的基本结构(构造组件)通常是一样的:以<html>开始</html>结束,在HTML部分中有<head>和</head>元素,在head部分中又有<title>和</title>元素,等等;但页面在表现上可以不同。每个页面有自己的页面标题、文本标题以及不同的body内容。此外,页面通常是经过多个步骤创建完成的:有一个函数添加页面标题,另一个添加主文本标题,还有一个添加页脚,等等。仅当一个页面的结构全部完成后,才能使用一个最终的渲染函数将该页面展示在客户端。我们甚至可以更进一步扩展这个HTML生成器,让它可以生成一些完全不同的HTML页面。一个页面可能包含表格,另一个页面可能包含图像库,还有一个页面包含联系表单,等等。
HTML页面生成问题可以使用建造者模式来解决。该模式中,有两个参与者:建造者(builder)和指挥者(director) 。建造者负责创建复杂对象的各个组成部分。在HTML例子中,这些组成部分是页面标题、文本标题、内容主体及页脚。指挥者使用一个建造者实例控制建造的过程。对于HTML示例,这是指调用建造者的函数设置页面标题、文本标题等。使用不同的建造者实例让我们可以创建不同的HTML页面,而无需变更指挥者的代码。
案例
让我们来看看如何使用建造者设计模式实现一个比萨订购的应用。比萨的例子特别有意思,因为准备好一个比萨需经过多步操作,且这些操作要遵从特定顺序。要添加调味料,你得先准备生面团。要添加配料,你得先添加调味料。并且只有当生面团上放了调味料和配料之后才能开始烤比萨饼。此外,每个比萨通常要求的烘焙时间都不一样,依赖于生面团的厚度和使用的配料。
完整示例代码(builder.py)如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2018/7/15 9:59
# @Author : Yaheng Wang (m13262578991@163.com)
# @Link : http://www.wy2160640.github.io
# @Version : 1.0 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:
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)) class MargaritaBuilder:
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):
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 mozzarella, 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 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):
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, mushroom, 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') class Waiter:
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 pizza(self):
return self.builder.pizza def validate_style(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()
小结
在以下几种情况下,与工厂模式相比,建造者模式是更好的选择。
1.想要创建一个复杂对象(对象由多个部分构成,且对象的创建要经过多个不同的步骤也许还需要遵从特定的顺序)
2.要求一个对象能有不同的表现,并希望将对象的构造与表现解耦
3.想要在某个时间点创建对象,但在稍后的时间点再访问
(转自精通Python设计模式)Python设计模式之创建型模式——2.建造者模式的更多相关文章
- JAVA设计模式 4【创建型】理解建造者模式
Hello,又是拖了几天更,实在是忙的要死,有时候忙累了,真的就是倒头睡的那种,刚好今天闲下来了.今天来更新一篇建造者模式. 其实建造者模式,我们已经在上一节已经有了解过了.只不过是上一节没有提到这样 ...
- 【设计模式】 模式PK:工厂模式VS建造者模式
1.概述 工厂方法模式注重的是整体对象的创建方法,而建造者模式注重的是部件构建的过程,旨在通过一步一步地精确构造创建出一个复杂的对象.我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方 ...
- 【设计模式】 模式PK:抽象工厂模式VS建造者模式
1.概述 抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可.而建造者模式则是要求按 ...
- java架构之路-(设计模式)五种创建型模式之单例模式
设计模式自身一直不是很了解,但其实我们时刻都在使用这些设计模式的,java有23种设计模式和6大原则. 设计模式是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可 ...
- 【python设计模式-创建型】工厂方法模式
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一.这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式. 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻 ...
- .NET设计模式 第二部分 创建型模式(3)—建造者模式(Builder Pattern)
建造者模式(Builder Pattern) ——.NET设计模式系列之四 Terrylee,2005年12月17日 概述 在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对 ...
- Java设计模式(5)——创建型模式之建造者模式(Builder)
一.概述 概念 将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示.(与工厂类不同的是它用于创建复合对象) UML图 主要角色 抽象建造者(Builder)——规范建造方法与结果 ...
- Java设计模式(2)——创建型模式之工厂方法模式(Factory Method)
一.概述 上一节[简单工厂模式]介绍了通过工厂创建对象以及简单的利弊分析:这一节来看看工厂方法模式对类的创建 工厂方法模式: 工厂方法与简单工厂的不同,主要体现在简单工厂的缺点的改进: 工厂类不再负责 ...
- PYTHON设计模式,创建型之工厂方法模式
我感觉和上一个差不多,可能不要动最要的地方吧... #!/usr/bin/evn python #coding:utf8 class Pizza(object): def prepare(self, ...
随机推荐
- htmlParser的使用-链接
基于htmlparser实现网页内容解析:http://www.cnblogs.com/coding-hundredOfYears/archive/2012/12/15/2819217.html ht ...
- 【转】HTTP方法
[转]HTTP方法 掌握HTTP虽然不是必须的,但是如果你知道它的工作原理,那么在学习JSP开发中的某些知识就可以易如反掌了. 一,HTTP协议详解之URL篇 http(超文本传输协议)是一个基于请求 ...
- CreateProcess 执行CMD命令,并重定向输出
1. 参考网址:http://www.cnblogs.com/cnarg/archive/2011/02/20/1959292.html function TfrmMain.ExecDosCmd :b ...
- HTTP- 头部信息
HTTP 头部信息由众多的头域组成,每个头域由一个域名,冒号(:)和域值三部分组成.域名是大小写无关的,域值前可以添加任何数量的空格符,头域可以被扩展为多行,在自每行开始处,使用至少一个空格或制表符. ...
- JavaScript中call、apply个人理解
JavaScript中call.apply个人理解 一句话即通俗的说:call.apply 是为了改变this的状态而存在的 }; } function personInfo(name,age){ t ...
- webpack 教程资源目录
初级教程 webpack-howto 作者:Pete HuntWebpack 入门指迷 作者:题叶 webpack-demos 作者:ruanyf一小时包教会 —— webpack 入门指南 作者:V ...
- spring boot: scope (一般注入说明(一) @Autowired注解)
实例一: DiConfig 文件: package di; import org.springframework.context.annotation.ComponentScan; import or ...
- ListOperations
RedisOperations<K,V> getOperations() V index(K key, long index) V leftPop(K key) V leftPop( ...
- java--xml文件读取(JDOM&DOM4J)
1.JDOM解析 首先导入额外的jar包: Build Path:jdom-2.0.6.jar 准备工做获取到子节点的集合: package com.imooc_xml.jdom.text; impo ...
- C++中函数重载
C++中函数重载使用顶层const修饰参数和不使用const修饰参数效果是一样的,如果定义了这样的重载函数会报函数重定义的错误. 追其原因,C++中的函数传递方式有三种,一种是值传递,就是拷贝,一种是 ...