《Python编程实战:运用设计模式、并发和程序库创建高质量程序》—— 1.2 建造者模式

简介:

本节书摘来自华章出版社《Python编程实战:运用设计模式、并发和程序库创建高质量程序》一 书中的第1章,第1.2节,作者:(美) Mark Summerfield,更多章节内容可以访问云栖社区“华章计算机”公众号查看。

1.2 建造者模式

“建造者模式”(Builder Pattern)与抽象工厂模式类似,都可以创建那种需要由其他对象组合而成的复杂对象。而建造者与抽象工厂的区别则在于,它不仅提供了创建复杂对象所需的方法,而且还保存了复杂对象里各个部分的细节。
此模式与抽象工厂模式一样,都能用来拼装对象(也就是用一个或几个简单的对象创建出复杂的对象),但前者尤其适用于需要把复杂对象各部分的细节与其创建流程相分离的场合。
我们用一段“表单”(form)生成程序来演示建造者模式,这段程序既可以用HTML生成网页表单,又可以通过Python及Tkinter生成GUI表单。两种表单都具备图形用户界面,用户可以向其中输入文本,但是表单上的按钮不起作用。图1.2演示了这两种表单,程序源代码位于formbuilder.py文件中。
screenshot

我们从最顶层的调用语句开始,看看构建每个表单所用的代码。
screenshot

上面这段代码创建了两个表单,并分别将其写入对应的文件中。在这两种情况下,都会调用同一个表单创建函数(也就是create_login_form()),每次调用时,传入与之相应的建造者对象。
screenshot

此函数可以创建HTML与Tkinter表单,而且只要有适当的建造者对象,它就能创建出任意形式的表单。builder.add_title()方法用于创建表单的标题,而其他方法则会把表单中的“控件”(widget)添加到给定的行、列位置上。
HtmlFormBuilder与TkFormBuilder都继承自抽象基类AbstractFormBuilder。
screenshot
screenshot

继承自AbstractFormBuilder的类必须实现所有抽象方法。上述代码没有列出add_entry()与add_button()这两个抽象方法,因为它们和add_label()方法非常相似,只是名字不同而已。另外,为了使用abc模块的@abstractmethod“修饰器”(decorator),我们必须把AbstractFormBuilder类的metaclass(元类)设置成abc.ABCMeta。(2.4节将详述修饰器。)

            序列与映射的解包操作

“解包”(unpacking)就是把“序列”(sequence)或“映射”(map)中的每个元素单独提取出来。“序列解包”(sequence unpacking)的一种简单用法是把首个或前几个元素与后面几个元素分别提取出来。比如像下面这样:

如果sequence里至少有三个元素,那么执行完上述代码之后,first == sequence[0],second == sequence[1],rest == sequence[2:]。
最常用到解包操作的地方可能是函数调用语句。如果函数接受一定数量的“位置参数”(positional argument)或某些特定的“关键字参数”(keyword argument),那么可以通过解包操作来提供这些参数。例如:

print_setup()函数需要两个位置参数(名为width及height),并且还有两个可选的关键字参数(名为copies与collate)。上面这段代码在调用函数时没有直接传递参数值,而是先创建了名为args的tuple与名为kwargs的dict,然后通过序列解包操作(args)与映射解包操作(*kwargs)来传递参数。这么做的效果与print_setup(600, 900, copies=2, collate=False)相同。
另一种与函数调用有关的用法是以解包符号来声明参数,使函数能够接受任意数量的位置参数或任意数量的关键字参数,也可以同时接受这两类参数。例如:

上面这段代码所声明的print_args()函数可以接受任意数量的位置参数或关键字参数。在函数中,args参数的类型是tuple,而kwargs参数的类型则是dict。如果还想在print_args()函数里用这些参数来调用别的函数,那么可以把带有解包符号的参数直接传进去(比如:function(*args, kwargs))。映射解包操作还有一种常见用法,就是拿它来调用str.format()方法。比方说,我们可以直接写s.format(locals()),而不用把每个参数都按照key=value的形式手工传进去(此用法的范例可参见1.1.1节的SvgText.__init__()方法)。

如果某个类的metaclass是abc.ABCMeta,那么该类就无法初始化了,只能把它当成抽象基类来用。把C++或Java代码移植到Python时,这么做确实很有用,不过会稍微增加运行期的开销。许多Python程序员都采用一种更为宽松的做法,那就是根本不使用metaclass,而是直接在文档中说明该类只能用作抽象基类。
screenshot

上面列出了HtmlFormBuilder类的前几行代码。如果构建表单时没有指定标题,那么__init__方法会提供默认的标题。表单里的控件都保存在名为items的字典中,字典的键是由row与column所构成的“二元组”(2-tuple),而值则是控件的HTML代码。
由于基类的add_title()方法是抽象的,所以本类必须重新实现它,不过在实现的时候,我们可以直接调用基类的实现代码。对于HtmlFormBuilder来说,在调用基类的add_title()之前,还必须先用html.escape()函数把title参数处理一下(在Python 3.2及之前的版本中,应该使用xml.sax.saxutil.escape()函数处理)。
add_button()方法(此方法没有列在上述范例代码中)的结构与其他以add开头的方法相似。
screenshot

HtmlFormBuilder.form()方法创建了一个HTML页面,其中有个

,而里又有个

上面节选了TkFormBuilder类的部分代码。我们把创建表单控件所用的语句(也就是以字符串形式保存的Python代码)放到列表中,每个控件用两条语句来创建。
add_entry()与add_button()方法的代码都没有列在上面,不过其结构与add_label()方法相同。这些方法都是先取得控件的“规范名称”(canonicalized name),然后再声明两个字符串:create字符串中的代码用于构造控件,而layout字符串中的代码则用于调整控件在表单中的位置。最后,这些方法都会把这两个字符串放到statements列表中。
form()方法非常简单,它用title及statements来填充TEMPLATE模板,并把填好的字符串返回。
screenshot

在这份模板代码中,我们先根据title来创建表单类(在本例中,title是Login,所以创建出来的类就叫做LoginForm,参见与)。__init__()方法先设置表单的标题(在本例中,标题是Login,参见),然后用statements中的那些语句来创建表单中的控件,并调整其位置()。
由于代码块最后有if __name__...等语句,所以用这份模板所生成的Python代码可以独立运行。
screenshot

为了使范例代码看起来完整一些,我们把_canonicalize()方法的代码也列出来。从这段代码本身来看,似乎每次执行此函数时都要重新创建正则表达式,但实际上Python有非常庞大的内部缓存,用于存放编译过的正则表达式。只要调用过_canonicalize()方法,那么后续调用时就可以直接从缓存中查找正则表达式了,不用再重新编译。

相关文章
|
3天前
|
Linux Python Windows
打包Python程序文件:pyinstaller实现
本文介绍基于Python语言中的pyinstaller模块,将写好的.py格式的Python代码及其所用到的所有第三方库打包,生成.exe格式的可执行文件,从而方便地在其他环境、其他电脑中直接执行这一可执行文件的方法。
|
4天前
|
机器学习/深度学习 网络协议 数据库
Python编程实战:解决常见编程问题
```markdown Python编程入门指南:涵盖文件操作、列表操作、字符串处理、函数编写、异常处理、网络编程和数据库操作等实战案例。通过示例代码,学习如何读写文件、排序列表、转换字符串、创建函数、处理异常、构建TCP服务器及操作SQLite数据库,逐步掌握Python核心技能。 ```
|
4天前
|
数据采集 JSON 数据库
800个程序实例、5万行代码!清华大学出版【Python王者归来】
Python 的丰富模块(module)以及广泛的应用范围,使 Python 成为当下最重要的计算机语言之一,本书尝试将所有常用模块与应用分门别类组织起来,相信只要读者遵循本书实例,定可以轻松学会 Python 语法与应用,逐步向 Python 高手之路迈进,这也是撰写本书的目的。 本书以约 800 个程序实例讲解了:完整的 Python 语法,Python 的输入与输出,Python 的数据型态,列表(list)、元组(tuple)、字典(dict)、集合(set),函数设计,类别设计,使用系统与外部模块(module),设计自己的模块(module),文件压缩与解压缩,程序除错与异常处理…
|
7天前
|
数据库连接 Python
如何提高python程序代码的健壮性
在编程的时候,我们难免会遇到一些不可靠的情况,比如网络请求失败,数据库连接超时等等。这些不确定性会让我们的程序容易出现各种错误和异常。那么如何来增加程序的容错性和健壮性呢? 可能大多数人会想到使用try except来进行异常捕捉进行失败重试(Retry)。虽然try-escept一个非常常见和有效的方式来增强程序稳定性,但是可能一不小心就会造成栈溢出。 所以接下来我就来介绍一个另外的一个专门用于失败重试的库:retrying。
|
7天前
|
存储 算法 Python
Python编程作业一:程序基本流程
Python编程作业一:程序基本流程
14 0
|
7天前
|
网络协议 Python
在python中利用TCP协议编写简单网络通信程序,要求服务器端和客户端进行信息互传。 - 蓝易云
在这个示例中,服务器端创建一个socket并监听本地的12345端口。当客户端连接后,服务器发送一条欢迎消息,然后关闭连接。客户端创建一个socket,连接到服务器,接收消息,然后关闭连接。
62 0
|
8天前
|
数据采集 安全 数据挖掘
2024年最新7 年 Python 的我,总结了这 90 条写 Python 程序的建议,上海大厂Python面试经历
2024年最新7 年 Python 的我,总结了这 90 条写 Python 程序的建议,上海大厂Python面试经历
2024年最新7 年 Python 的我,总结了这 90 条写 Python 程序的建议,上海大厂Python面试经历
|
8天前
|
Python
2024年最新【Python】程序的组织结构:顺序结构,2024年最新46道面试题带你了解中高级Python面试
2024年最新【Python】程序的组织结构:顺序结构,2024年最新46道面试题带你了解中高级Python面试
2024年最新【Python】程序的组织结构:顺序结构,2024年最新46道面试题带你了解中高级Python面试
|
8天前
|
机器学习/深度学习 数据采集 数据挖掘
90%的人说Python程序慢,5大神招让你的代码像赛车一样跑起来_代码需要跑很久怎么办(2)
90%的人说Python程序慢,5大神招让你的代码像赛车一样跑起来_代码需要跑很久怎么办(2)
|
9天前
|
机器学习/深度学习 数据采集 Java
如何提高Python程序的性能
Python作为一种高级编程语言,具有易学易用、开发效率高等优点,但其在性能上可能不如C++或Java。本文将介绍如何通过一些技巧和工具来提高Python程序的性能。