实战Python设计模式:可复用面向对象软件开发实践(txt+pdf+epub+mobi电子书下载)


发布时间:2021-03-09 09:00:42

点击下载

作者:薛卫国,薛卫民

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

实战Python设计模式:可复用面向对象软件开发实践

实战Python设计模式:可复用面向对象软件开发实践试读:

前言

写书不是一件容易的事,因为许多自以为想得很清楚的事情,一旦用文字来表达总感觉有些语焉不详。同时,将自己写的代码公开需要很大的决心,代码的规范性、正确性等都是需要考虑的问题。幸运的是,各位读者还是看到了这本书。笔者认为,对于那些刚接触面向对象开发的人,或者已经具备基本知识却不知道如何运用的开发者,拥有一本结合开发实践Python设计模式的书是很有必要的。笔者结合自己二十多年的开发工作经验,将关于面向对象和设计模式的知识通过Python语言展示给各位读者。面向对象、设计模式的初学者通过本书可以快速入门;已经基本掌握设计模式的开发者通过本书可以理解设计模式在实际开发中的运用,从而能够合理、有效地选择设计模式。

关于设计模式

在实际开发中运用设计模式有利于设计出扩展性更好、更规范的软件架构。在软件开发过程中,总有部分内容是设计者凭直觉得出的结论,但是凭直觉得出的结论不可避免地会出现不易理解的内容。通过运用设计模式,可以在另一个高度上审视自己的设计。面向对象的开发很多时候是基于规则的,越早使用规则,越能在早期发现问题,越能开发出独立性强、扩展性好的软件,而设计模式就是经过实践证明的诸多规则的最佳实践。

理解了设计模式相当于掌握了一种更高级的设计语言。在考虑设计方案时,一个设计模式的名称就可以表达很多信息,这样可以大大节约设计的时间,提高沟通的效率。笔者身边很多程序员的记忆力非常好,很擅长调试程序,但是让他们描述自己的设计,他们却会感觉很困难。这时如果使用设计模式语言,就可以大大改善这种状况。设计模式语言可以让开发者之间的沟通更顺畅,让设计资料的思路更清晰。

本书的构成

在本书中提到的设计模式都是面向对象领域耳熟能详的内容,为了让读者更好地理解这些内容,本书通过如下方式对其进行说明。

1.问题说明

本书力争通过PyExecutor中的实例,使读者理解设计模式想要解决的问题。问题说明部分的内容可以看作设计模式的运用场景,而理解场景和设计模式的关系正是选择和运用设计模式的关键。当然,设计模式的运用场景并不限于问题说明中的实例,这一点请读者务必理解。

2.模式结构

设计模式是若干类协同完成复杂功能的方式,模式结构用来说明设计模式中每个类的职责、分工,以及类与类之间是如何协同工作的。读者通过阅读这部分内容,可以对设计模式有一个大致理解。模式结构重点说明的是设计模式的静态信息,其动态信息可以结合示例代码进行理解。

3.示例代码

为了让读者对模式结构中的内容加深理解,示例代码会提供可以执行的代码。代码的内容可能是模式结构的简单实现,也可能会结合一个简单的示例,总之不会离设计模式本身的内容太远。所有代码都是可以执行的,读者最好在自己的电脑上输入并调试这些代码,这样可以加深对设计模式的理解,因为很多东西只有在程序运行时才能看出来。

4.实战运用

示例软件PyExecutor包含了书中介绍的23个设计模式。如果原封不动地照搬设计模式,是无法获得理想的结果的。无论是设计模式本身,还是运用设计模式的对象,都需要根据实际情况进行相应调整,实战运用中的内容就是这方面的示例。由于PyExecutor的规模比较大,所以本书不会展示其所有代码,读者最好通过下载链接获得代码,再结合书中的说明,边调试边理解代码。

5.效果讨论

效果讨论主要用来说明使用设计模式可以解决的问题,以及设计模式为软件带来的优势。通过对示例的说明,读者已经对设计模式有了自己的认识,这时再去理解设计模式,可以达到事半功倍的效果。

6.提示和技巧

任何事物都不可能只有优点没有缺点,设计模式也是如此。提示和技巧一方面会说明使用设计模式的注意事项,另一方面也会提供一些深入运用设计模式的建议。

阅读本书的几点建议

设计模式多种多样,其本质就是对需要替换的、变更的内容进行抽象处理,然后再运用多态技术对功能进行扩展,简单地讲就是,想变什么就抽象什么,这应该是学习和理解设计模式的最重要的9个字了。

在学习设计模式时经常遇到的一个问题就是,很多设计模式的类图都很相似,在实际运用时不知道应该选择哪一个。出现这个问题的最大原因就是将类图当成选择设计模式的依据了。由于面向对象设计唯一的“大招”就是多态,所以很多设计模式的构造看起来或多或少都有些相似。比较合理的步骤是先根据场景选择设计模式,然后根据设计模式构造(形式)调整类的职责、接口和协作关系。

有些开发者反对设计模式的一个原因就是,使用设计模式容易出现过度设计。关于这个问题,笔者的观点是:过度设计当然不好,但是这并不是设计模式的错,而是开发者的问题。这和难以理解的接口、混乱的代码是同样的问题。为避免过度设计就不花时间和精力使用设计模式进行设计,就有点因噎废食了。

关于类图

本书中的类图都是按照UML规范绘制的,但是考虑到不是所有读者都掌握了UML知识,所以这里对其进行简单说明。

图0-1中的每个方框均代表一个类,这个类被横线分为上下两部分:横线上面是该类的类名,横线下面是该类的多态操作(斜体)或方法(正体)。UML类图也有分为上、中、下三部分的情况,这时中间的区域显示的是类的属性。

图0-1中的带箭头的虚线表现的是依赖关系,在大多数情况下它表明一个类会使用另一个类的功能。图0-1中Director类指向Builder类的依赖关系表明Director类会使用Builder类的功能。

图0-1中带空心三角形的有向虚线表现的是实现关系,这种关系的含义是一个类可以实现另一个类定义的接口。例如,图0-1中ConcreteBuilder1类指向Builder类的实现关系,表示ConcreteBuilder1类会实现Builder类定义的接口。但是Python语言比较特别的一点是:具象Builder不一定需要一个具体的抽象Builder类,只要实现者、利用者遵守相同的约定,即可实现利用者和提供者之间的调用关系。这种情况在Python语言中有一个定义—— protocol。对于这种可选的抽象基类,本书会指定一个<>衍型来表明这种关系。图0-1 类图示例1

除此之外,本书还用到了继承关系,它表现为带空心三角形的实线,这种关系在UML中被称为泛化,在图0-2中Leaf类指向Component类的关系就属于这种情况。这种情况也可以说Leaf类是Component类的派生类。图0-2 类图示例2

类之间的实线表示这两个类之间存在关联关系,即这两个类需要协同工作。图0-2中带有一个箭头的实线表明两个类间的这种关联关系是单向的,而没有箭头的实线表明两个类间的这种关联关系是双向的。

图0-2中一端为实心菱形的关联关系表明整体和部分的关系,其中实心菱形端是整体。这种关系表明当整体消失时,部分也会随着消失,这种关系被称为组合关系。组合关系类似于笔记本电脑和屏幕的关系。部分和整体的关系也有用空心菱形表示的,虽然其含义和组合关系的含义相似,但是整体和部分可以单独存在,这种关系被称为聚合关系。聚合关系类似于台式电脑和显示器的关系。

程序员的语言

将自己写的代码公开需要很大的勇气,有很多程序员甚至不敢在自己写出的代码上签名。在本书中使用的PyExecutor是笔者为了说明设计模式而开发的一个模拟项目,由于时间和能力的限制,书中的代码只写到能将设计模式讲清楚的程度,离真正的产品还有很远的距离。如果读者希望将这些代码运用到自己的工作中,那么还需要对其进行完善和改进。书中关于设计模式、Python语言和tkinter库的用法也有不足之处,恳请各位读者批评指正。

致谢

感谢电子工业出版社的林瑞和编辑。本书从立意、结构的确定到内容的确认和整理,每个环节都得到了林编辑的建议和意见。毫不夸张地说,如果没有林编辑的努力,就不会有本书的面世。

感谢我的父母。他们两个人都没有读过什么书,但是他们不遗余力地培养孩子读书,时至今日,我们兄弟几人虽说没有出人头地,但做到了自食其力。

感谢我的夫人。夫人本来也是一个学霸,自结婚以后,她将大部分时间都花在家庭、孩子上,但是她一直无怨无悔。

感谢我的两个孩子。孩子们为这个家庭带来了活力,他们在自己成长的同时也见证了父母的成长。在本书写作过程中,他们也做出了自己的贡献。目前两个孩子都处在成长的重要阶段,在此希望我可以为他们树立榜样。薛卫国【读者服务】

微信扫码回复:37959

●获取本书配套代码资源

●获取博文视点学院20元付费内容抵扣券

●获取更多技术专家分享视频与学习资源

●加入读者交流群,与更多读者互动第1章 示例软件介绍

为了使读者更容易理解将要说明的设计模式,本书将通过一个具体的Python语言开发案例来对其进行讲解。希望这个案例可以帮助读者理解和掌握在实际开发过程中选择和使用设计模式的方法。

我们的目标是用Python语言开发一个功能模块图(Function Block Diagram)编辑和调试软件。利用这款软件,用户可以设计和调试小到建模仿真,大到洗衣机、冰箱控制的运算逻辑。1.1 准备工作1.1.1 相关资源下载

本书说明的PyExecutor软件的源代码、各设计模式的示例代码及使用PyExecutor设计的示例逻辑,读者都可以通过前面的二维码下载。结合这些资源学习本书中的内容,读者可以取得事半功倍的效果。1.1.2 安装开发环境

如果只是执行本书中说明的程序,那么只需要访问Python官网下载最新的安装包并安装即可。

为了更好地理解和调试书中的代码,读者最好安装一个IDE开发环境,作者推荐PyCharm,这个软件可以通过官方网站下载。PyCharm的版本分为专业版和社区版,读者访问PyCharm网站下载社区版即可满足本书需求。1.2 启动

在使用命令行模式时,进入PyExecuter\PyExecutor目录,并执行代码1-1即可启动PyExecutor。

代码1-1 PyExecutor启动命令

在PyCharm中启动PyExecutor可能需要做一些准备工作,但启动方式还是执行相同的ExecutorMain.py文件。1.3 功能说明

目前PyExecutor支持两种编程逻辑,因此在启动PyExecutor时将显示如图1-1所示的逻辑类型选择界面,让用户选择想要编辑的逻辑类型。图1-1 逻辑类型选择界面

如果用户选择“Electric Appliance Control”模式,那么PyExecutor将进入小家电控制逻辑设计状态,这时PyExecutor的界面如图1-2所示。在这个状态下,用户可以使用与小家电控制相关的控制模块来构建自己的控制逻辑。图1-2 小家电控制逻辑设计界面

如果用户选择“Function Block Diagram”模式,那么PyExecutor将进入通用功能模块逻辑设计状态,这时PyExecutor的界面如图1-3所示。在这个状态下,用户可以使用数据处理逻辑模块来构建自己的运算或仿真逻辑。图1-3 通用功能模块逻辑设计界面1.4 功能概要

接下来以功能模块图为例来说明PyExecutor的功能。功能模块图支持的基本功能有三角函数信号发生器、滤波器、数学运算、通信、数字和趋势曲线表示功能。除此之外,PyExecutor还支持通过组合标准功能模块来定制新功能模块。软件的架构已经为功能扩展预留了接口,用户可以很方便地增加其他高级功能,如控制逻辑、AI处理模块等。既可以通过工具栏来选择这些功能模块,也可以通过菜单栏来选择这些功能模块。1.4.1 编辑

下文将以构建如图1-3所示的运算逻辑为例来说明PyExecutor的基本用法。

选中工具栏中的“Gentor”之后,在画面适当的位置双击就可以添加信号发生器模块。

在信号发生器模块上右击,即可打开如图1-4所示的属性设定界面。在“共通”属性页中将模块名称修改为“Sin1”后,进入“参数”属性页,将“振幅”设定为“3”,“周期”设定为“30”。最后单击“Apply”按钮保存修改的内容。图1-4 信号发生器模块的属性设定界面

增加另外一个信号发生器Sin2,并将其“振幅”设定为“3”,周期设定为“50”。

以同样的方法添加数学运算模块Math、数值表示模块ValPanel和图形表示模块Graph。其中,数值表示模块ValPanel的表达式按照图1-5中的内容设定。图1-5 表达式设定界面

添加并设置完功能模块后,我们将得到如图1-6所示的运算逻辑。图1-6 增加功能模块

接下来使用连接线建立功能模块之间的数据流。为了界面整洁,PyExecutor只支持直角折线。双击Sin1模块的Out端口开始添加连接线,在连线途中单击即可增加一个拐点,最后双击Math模块的In1端口结束连线。这个操作的含义就是Sin1模块的输出数据被传递到Math模块作为In1使用。使用同样的方式在其他模块之间添加连线之后得到如图1-7所示的运算逻辑。图1-7 建立连接关系

在增加模块和建立模块之间的连接关系的过程中,用户可以使用鼠标调整功能模块和连接线的位置,也可以通过右击启动上下文菜单来设定功能模块的属性或者删除功能模块和连接线,也可以通过复制和粘贴已有的功能模块来提高设计运算逻辑的效率。

对于用户的所有编辑操作,PyExecutor支持不限步数的撤销和重做。这里的操作指的是功能模块的增加、移动和删除,连接线的增加、调整和删除,逻辑要素的属性设定,等等。用户通过单击工具栏上的“Undo”按钮和“Redo”按钮进行撤销和重做操作。1.4.2 逻辑调试

编辑工作完成后,可以通过PyExecutor的调试功能对逻辑的正确性进行确认。用户可以通过“Debug”菜单中的“Start”菜单项启动逻辑调试。当PyExecutor处于如图1-3所示的调试状态时,用户可以通过数字面板模块和曲线模块来实时呈现运算结果。PyExecutor支持在调试过程中修改功能模块的设定值、连接线,使用者在修改逻辑内容之后可立即看到计算结果。

调试工作结束后,用户可以使用“Debug”菜单中的“Stop”菜单项停止逻辑调试。1.4.3 文件管理

用户通过文件管理功能可以以JSON形式保存设计完成的逻辑,也可以打开已经保存的逻辑并进行再次编辑。文件打开时系统会根据文件类型自动选择PyExecutor的编辑模式。1.4.4 代码生成

PyExecutor还支持将完成设计和调试的逻辑生成实际可以执行的代码,用户可以通过“Build”菜单选择代码的生成形式。目前PyExecutor支持生成Python语言和C/C++语言的代码。用户也可以直接将这些代码合并到自己的系统中,以获得更好的执行效率。第2章 创建型模式2.1 抽象工厂模式2.1.1 问题说明

PyExecutor支持通用功能模块逻辑和小家电控制逻辑的设计。在软件启动时,用户可以根据自己的需要通过如图2-1所示的界面选择运算逻辑的类型。图2-1 逻辑类型选择界面

用户如果选择“Function Block Diagram”,那么将进入PyExecutor的通用功能模块逻辑设计界面,如图2-2所示。

运算逻辑首先使用两个信号发生器生成不同的正弦数据,然后使用Math模块对这两个数据进行数学运算,最终将结果输出到Graph模块中进行表示。

用户如果选择“Electric Appliance Control”,那么将进入PyExecutor的小家电控制逻辑设计界面,如图2-3所示。

小家电控制逻辑使用OpPanel功能模块设定运行模式并控制运行状态;使用TempSensor模块获得实际温度。将这两个模块的输出连接到Controller模块,经过Controller模块的运算得到控制量并将其输出给Heater模块。与此同时Display模块将接收Controller模块和TempSensor模块的输出数据并将其显示在小家电的显示屏上。

PyExecutor的架构需要解决的问题是,如何通过合理的结构简单地支持这种切换。通过比较可以发现,这两种逻辑的相同点是都使用功能模块来表现具体的操作步骤,都使用连接线来表现数据的传递;两者的不同点是功能模块的种类和功能有区别。因此,需要用一种方式将不同的功能模块组引入软件,以构建不同形式的运算逻辑。

我们使用抽象工厂模式来解决这个问题。图2-2 通用功能模块逻辑设计界面图2-3 小家电控制逻辑设计界面2.1.2 模式结构

当我们希望成组替换生成的产品的时候,可以考虑使用抽象工厂模式。抽象工厂模式类如图2-4所示。图2-4 抽象工厂模式类图

抽象工厂类AbstractFactory用来定义工厂类的接口。在Python语言中这个类并不是必须定义的,只要在需要实现共通功能的时候定义即可,这样可以提高代码的复用性。

具象工厂类(ConcreteFactory1或者ConcreteFactory2)负责根据扩展的需求构建一组实际的产品。为了明确类的职责,类名可以将Factory作为后缀。

和抽象工厂类的情况类似,Python语言中的抽象产品类ProductA、ProductB也不是必须定义的,只要每个工厂方法生成的产品类支持相同的操作即可(Python语言称这种情况为Protocol),这一点和模板有些类似。

具象产品类(ConcreteProductA1、ConcreteProductA2、ConcreteProductB1、ConcreteProductB2)遵从抽象产品类的约定并实现自己的功能。PyExecutor中和具象产品类相对应的是通用功能模块逻辑和小家电控制逻辑中的各种功能模块及连接线。2.1.3 示例代码

接下来以一个汽车组装厂为例来说明抽象工厂模式示例代码。这个组装厂可以生产两种车型,即小汽车和卡车。生产小汽车需要小型车体和小型车轮,代码2-1中分别用MiniBody类和MiniWheel类来表现。

代码2-1 MiniBody类和MiniWheel类

生产卡车需要重型车体和大型车轮,代码2-2中分别用HeavyBody类和BigWheel类来表现。

代码2-2 HeavyBody类和BigWheel类

由代码2-1和代码2-2可知,不同类型的车轮类和不同类型的车体类之间不存在共同的基类,在Python语言中它们只需要支持同样的接口即可。产品类准备好之后就可以开始实现工厂类了。代码中需要为每一种车型准备一个为其提供零件的工厂,具体实现如代码2-3所示。与产品类的情况类似,工厂类也不一定必须从同一个基类继承。

代码2-3 零件工厂类

工厂类和产品类准备完毕后就可以开始生产汽车了。由于有了前面的准备工作,无论是哪种类型的车辆都可以使用相似的过程进行组装,车辆组装的实现如代码2-4所示。

代码2-4 车辆类和组装方法

代码2-4先定义了一个通用的车辆类,然后根据输入的车体对象和车轮对象组装车辆。在这个过程中不需要知道具体的工厂和车辆零件的类型,这保证了组装过程的可扩展性。

代码2-5使用相同的组装方法分别组装了一辆小汽车和一辆卡车。

代码2-5 组装车辆代码

抽象工厂模式示例代码的输出结果如代码2-6所示。

代码2-6 抽象工厂模式示例代码的输出结果

由代码2-6可知,由于引入了抽象工厂模式,代码可以以相似的方式生成不同类型的产品。2.1.4 实战运用

在PyExecutor中实现切换功能模块组的第一步是,定义一个用来引入功能模块组的抽象工厂类,其实现如代码2-7所示。

代码2-7 抽象工厂类

make_connector方法用来生成各个功能模块之间的连接线。element_types方法用来返回工厂类可以生成的功能模块类型列表,软件架构根据这个列表生成工具栏和菜单。make_element方法根据type参数提供的信息可以生成实际的功能模块。抽象工厂类分别为element_types方法和make_element方法提供了默认的实现,这样一来具象工厂类就不再需要为共通的内容提供实现了。make_context方法的作用是生成调试时需要的上下文。

需要补充的是,代码2-7中的make_connector方法和make_context方法的实现都是升起一个NotImplementedError异常。这样做是为了防止具象工厂类忘记实现必要的功能,毕竟异常不是那么容易被忽视的。

接下来具象类的责任则是针对具体运算逻辑类型的需要,引入相应的功能模块。

代码2-8中的make_connector方法和make_context方法根据要求分别生成具体类型的实例,而element_types方法则先生成自身可以提供的功能模块列表,然后取得基类的功能模块列表并将其添加到自身的功能列表中,最后将该列表返回。make_element方法根据参数type返回指定的功能模块,当遇到不能处理的类型时,则直接调用基类的make_element方法。

代码2-8 FbdComponentFactory类

准备工作完成后,就可以使用具象工厂类了。首先构建工具栏和菜单,其实现如代码2-9所示。

代码2-9 构建工具栏和菜单

代码2-9中的黑体部分通过element_types方法取得要素的列表后,生成如图2-5所示的菜单和工具栏。代码2-9通过lambda表达式来动态生成必要数量的菜单项和工具按钮。图2-5 通用功能模块逻辑编辑界面

用户通过菜单或工具栏选定功能模块时调用的事件处理代码如代码2-10所示。

代码2-10 用户通过菜单或工具栏选定功能模块时调用的事件处理代码

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载