软件架构:Python语言实现(txt+pdf+epub+mobi电子书下载)

作者:(印)阿南德·巴拉钱德拉·皮莱

出版社:机械工业出版社

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

软件架构:Python语言实现

软件架构:Python语言实现试读:

前言

软件架构,可以说是为特定的应用软件创建一个蓝图设计。软件架构中存在两大挑战:首先,软件架构与需求必须保持一致,对尚未发现的需求或者发生演化的需求都是如此;其次,尽管常常发生架构实现的变更,但软件架构与其对应的架构实现必须保持一致。

本书包含很多示例和用例,通过这种直观的方法来帮助你获取成为一名成功的软件架构师所需的一切。本书将帮助你了解Python的来龙去脉,以便可以用Python来构建和设计高度可扩展的、健壮的、简洁的、性能强大的应用程序。主要内容

第1章 介绍了软件架构的核心思想,简要介绍了架构质量属性和一些隐含的原理。这将使你能够在软件架构原理和基本属性方面拥有良好的知识基础。

第2章 包括开发中软件架构的可修改性和可读性。它将帮助你深入理解架构的可维护性等质量属性,并获得用Python编写代码来测试应用程序的各种技巧和策略。

第3章 帮助你理解软件架构的可测试性,以及如何为Python应用程序构建架构以满足可测试性。你还将了解可测试性和软件测试的各个方面,以及Python中可用的各种库和模块,以便编写各种可测试的应用程序。

第4章 讨论了在编写Python代码过程中关于性能的方方面面。你不仅可以学习架构性能的基本知识,还可以掌握在何时何地需要进行性能优化。例如,你会学习到何时进行SDLC的性能优化。

第5章 不仅阐述了编写可扩展应用程序的重要性,还讨论了实现应用程序可扩展性的各种不同方法,并论述了如何利用Python来实现各种可扩展性技术。你不仅能学到可扩展性的理论方面的知识,还能学到业界的最佳实践。

第6章 讨论了架构安全性的方方面面,并使你掌握一些最佳实践和技巧来编写安全性高的应用程序。你会了解在Python架构应用程序中可能出现的各种不同的安全问题,以及Python是如何从头开始保障安全性的。

第7章 从程序员实用性的角度,简要论述了Python中出现的各种设计模式以及每个模式的理论背景。这些设计模式对程序员来说是非常实用的。

第8章 从较高抽象层次角度介绍Python中现有的架构模式,同时给出了几个示例,用来说明如何利用Python库和框架来实现基于这些模式的高层次架构问题的解决方法。

第9章 讨论如何正确地在远程环境中或云上使用Python轻松部署代码的方方面面。

第10章 讨论了一些Python代码调试技术,包括最简单实用的打印语句、日志记录和系统调用跟踪机制等,这些对程序员来说都是非常容易获得的,也有助于系统架构师指导他的团队。阅读本书需要准备什么

为运行本书中展示的大部分代码示例,需要在系统中安装Python 3。其他的预备知识会在相应的实例中提到。本书的读者对象

本书适用于有经验的Python开发人员,他们渴望成为企业级应用程序的架构师;本书也适用于软件架构师,他们希望利用Python的特长来创建更有效的应用程序蓝图。约定

书中的代码块设置如下:

当希望重点关注代码块的某个特定部分时,会将相关的行(line)或项(item)设置成粗体:

任何命令行输入或输出写成如下形式:

新术语和重要词汇以粗体形式表示。示例代码下载

可以从http://www.packtpub.com下载本书的示例代码文件(需要Packt账户)。也可以访问http://www.packtpub.com/support并注册账户,Packt将通过email把文件直接发送给你。

可以按照以下步骤下载代码文件:(1)使用你的电子邮件地址和密码注册或登录到我们的网站。(2)将鼠标指针悬停在顶部的SUPPORT选项卡上。(3)单击Code Downloads & Errata选项。(4)在Search框中输入图书的名称。(5)选择要下载代码文件的书。(6)从下拉菜单中选择本书的购买渠道。(7)单击Code Download下载。

你还可以单击Packt Publishing网站中图书页面上的Code Files按钮下载代码文件,也可以通过在Search框中输入图书的名称来访问此页面。请注意,你需要首先登录Packt账户。

下载文件后,请确保使用这些最新版软件来解压缩文件或文件夹:

·WinRAR/7-Zip for Windows

·Zipeg/iZip/UnRarX for Mac

·7-Zip/PeaZip for Linux

该书的代码包也由GitHub托管在https://github.com/PacktPublishing/Software-Architecture-with-Python。你还可从Packt提供的图书和视频目录中获取其他代码包,网址为https://github.com/PacktPublishing/。第1章软件架构原理

这是一本关于Python的书,同时也是一本关注软件开发全生命周期中软件架构及其各种属性的书。

为了便于读者理解这两个方面,并使之有机结合(这是从本书中受益的关键),需要掌握如下三个方面内容:软件架构的基础知识,与之相关的主题和概念,以及软件架构的各种质量属性。

实际软件开发工作中,许多在其团队中担任重要角色的软件工程师往往对软件设计、架构的定义,以及他们在构建可测试、可维护、可扩展、安全和功能性软件中的角色有不同的解释。

尽管有大量的讨论软件架构的文献,包括传统的纸书和互联网上的电子资料,但很多时候,仍然有很多实践者难以掌握某些非常重要的概念。这通常是因为他们迫切要学习的是架构技术,而忽视了在构建系统过程中所需的基本设计和架构原理的学习。在软件开发组织中这是一种常见的现象或做法,因为对他们来说交付运行代码的压力往往超过其他一切。

本书力图超越中间路径,在软件开发中,将架构质量属性相关的深奥理论与使用编程语言、库和框架构建软件的平凡细节相结合——希望在这种情况下,使用Python及其开发者生态系统。

本章尽量用非常简单明了的术语向读者解释一些基本概念,目的是为读者理解本书其余部分奠定基础。希望读到本书的结尾,读者会发现这些概念和实践细节表达了一个连贯的知识体系。

接下来进入正题,本章大致将分为以下部分:

·软件架构定义

·软件架构与设计

·软件架构相关的几个方面

·软件架构的特征

·软件架构的重要性

·系统架构与企业架构

·架构的质量属性

·可修改性

·可测试性

·可扩展性/性能

·安全性

·可部署性1.1 软件架构定义

相关文献中有各种对软件架构的定义,其中一个简单的定义如下:

软件架构是对软件系统的子系统或组件以及它们之间关系的描述。

下面是《Recommended Practice for Architectural Description of Software-Intensive Systems(IEEE)technology》给出的一个更为正式的定义:“架构是一个系统的基本组织结构,涵盖所包含的组件、组件之间的关系、组件与环境的关系,以及指导架构设计和演进的原则等内容。”

如果在网上搜索,会得到大量的关于软件架构的定义。这些定义可能看起来有所不同,但是所有的定义都指向软件架构背后的一些核心的或基本的方面。1.1.1 软件架构与设计

根据作者的经验,针对软件架构和软件设计这两个概念的讨论,似乎在线上论坛和线下论坛中经常出现。因此,先花点时间来讨论如何理解这方面的问题。

虽然这两个术语有时可互换使用,但是系统架构与系统设计还是存在着区别,可以粗略概括如下:

·架构关注如何对系统中的结构和交互进行较高级别的描述,它关注的是那些与系统骨架相关的决策问题,例如,功能、组织、技术、业务和质量属性等。

·设计关注构成系统的部件或组件,以及子系统是如何组织的,这里关注的问题通常更接近所讨论的代码或模块自身,例如:

·将代码分成哪些模块?如何组织?

·给不同的功能分配哪些类(或模块)?

·对于类“C”应该使用哪种设计模式?

·在运行时对象之间是如何交互的?传递什么消息?如何实施交互?

软件架构关注整个系统的设计,而软件设计更多地是关注设计细节,通常是指组成这些子系统的各种更低层次子系统和组件的实现级的细节问题。

换句话说,在软件架构和软件设计中有“设计”的意思,但与后者相比,前者具有更高的抽象性和更广的范围。

软件架构和软件设计都有丰富的知识体系可用,分别是架构模式和设计模式。本书将在后面的章节中详细讨论这两个主题。1.1.2 软件架构相关的几个方面

无论是在IEEE给出的正式定义中,还是前面提到的相对来说非正式的定义中,都会发现一些共同的、重复的词汇,为了进一步讨论软件架构,对它们的准确理解是非常重要的:

·系统:系统是以特定方式组织的组件集合,以实现特定的功能。软件系统是其软件组件的集合。一个系统通常可以划分成若干个子系统。

·结构:结构是根据某个指导规则或原则来组合或组织在一起的一组元素的集合。元素可以是软件或硬件系统。软件架构可以根据观察者的上下文展示各个层次的结构。

·环境:软件系统所在的上下文或环境对其软件架构有直接的影响。这样的上下文因素可以是技术、商业、专业、操作等。

·利益相关者:任何对某个系统及其成功与否感兴趣或关心的个体或团体,都是利益相关者。例如,架构师、开发团队、客户、项目经理和营销团队等。

既然已经了解软件架构的一些核心方面,现在简要地列出软件架构的一些特征。1.2 软件架构的特征

所有软件架构都具有一组共同的特征,来看看其中一些最重要的架构特征。1.2.1 用架构来定义一种结构

某个系统的架构是该系统结构细节的最好表示方式。为了表示子系统之间的关系,工程师通常将系统架构绘制为结构化的组件图或类图。

例如,以下架构图描述了一个应用程序的后端,它从使用ETL进程加载的分层数据库系统中读取数据。

结构(structure)提供了一种深入理解架构的视角,也提供一种独特的视角来分析架构及其质量属性。

一些结构示例如下:

·运行时结构。在运行时创建的对象及其之间的交互方式经常决定部署架构(deployment architecture)。部署架构与可扩展性、性能、安全性和互操作性等质量属性密切相关。

·模块结构,为了分解任务,如何拆分代码并把代码组织到模块和包中,这与系统的可维护性和可修改性(可扩展性)密切相关。因为:

·在代码组织过程中考虑到可扩展性的话,通常会将父类放在单独定义好的具有恰当文档和配置的包中,这样就可以轻易地通过增加外部模块进行扩展,而不需要处理太多的依赖关系。

·对于那些依赖于外部或第三方开发者(库、框架等)的代码,通常会根据提供的安装或部署步骤,从外部源手动或自动地获取并补全各种依赖。此类代码还提供多种文档(例如README、INSTALL等),它们清楚地记录了这些步骤。1.2.2 由架构来挑选一组核心元素

一个定义良好的架构只会捕获那些构建系统核心功能所需的核心结构元素,这些核心结构元素一般会对系统有持久的影响,绝不是像流水账似地记录系统每个组件的所有细节。

例如,某个架构师正在描述用户与Web服务器交互以浏览Web页面的架构——这是一个典型的C/S(客户端–服务器)架构——将主要关注两个组件:用户的浏览器(客户端)和远程Web服务器(服务器)。它们构成了系统的核心元素。

系统可能有其他组件,例如在从服务器到客户端的路径中用到的多个缓存代理,或者服务器上的远程缓存,它们是用来加快Web页面的传送速度。但是,这些都不是架构描述的关注点。1.2.3 由架构来捕获早期的设计决策

这是基于前文所描述的架构特征得到的推论。能够帮助架构师专注于系统的一些核心元素(及其交互)的决策一定是系统早期设计决策的结果。这些决策从一开始就占有重要分量,在系统的后续开发中充当重要角色。

例如,在对某个系统的需求进行仔细分析之后,架构师可能会做出以下早期设计决策:

·系统将只部署在Linux 64位服务器上,因为这满足了客户端需求和性能约束。

·系统将使用HTTP作为实现后端API的协议。

·该系统将尝试对API使用HTTPS,将敏感数据从后端传递到前端时,要使用2048位或更高的加密证书。

·系统后端的编程语言将是Python,前端编程语言是Python或Ruby。

注意:第一个决策在很大程度上将系统的部署选择固定在特定的操作系统和系统架构上。接下来的两个决策对如何实现后端API有约束。最后一个决策确定了系统的编程语言选择。

早期的设计决策需要在仔细分析需求之后才能做出,同时满足一些约束和限制条件。这些约束和限制条件包括组织的、技术的、人员的和时间的,等等。1.2.4 由架构来管理利益相关者的需求

一个系统的设计和构建终究是在其利益相关者的需求下进行的。然而,这些需求有时相互矛盾,因此每个利益相关者的需求无法全部满足。例如:

·营销团队关注的是有一个功能齐全的应用软件,而开发团队在添加特征(feature)时关注的是特征演化和性能问题。

·系统架构师关心的是如何使用最新技术将部署扩展到云上,而项目经理则担心如果以这类新技术来部署会对预算产生多大影响。终端用户关心的是功能的正确性、性能、安全性、可用性和可靠性等问题,而开发组织(架构师、开发团队和管理人员)关心的是在交付所有特性的同时项目的进度和预算不受影响。

·好的架构会通过折中尽力平衡这些需求,并交付具有良好质量属性的系统,同时兼顾人员和资源成本的限制。

·架构提供了利益相关者之间的一种通用的语言,使利益相关者能够通过这种约束性的表达来进行有效的沟通,并帮助架构师实现可最有效捕获这些需求和平衡点的架构。1.2.5 架构影响着组织结构

架构描述的系统结构通常可以直接映射到系统构建团队的结构。

例如,架构可以具有数据访问层,用以描述一组读取和写入大量数据的服务——很自然地会想到把这样的系统按功能分配给已具备相关技能的数据库团队。

由于系统的架构最好以自顶向下的结构描述,它也经常作为任务分解结构的基础。因此,软件架构往往直接影响着构建它的组织结构:Web搜索应用程序的系统架构如下图所示。

下图显示了从架构到构建此应用程序的团队结构的映射。1.2.6 架构受到环境的影响

环境(environment)会对架构施加外部约束或限制,架构必须在这样的环境中发挥作用。在文献[Bass,Kazman]中,环境通常被称为上下文架构(architecture in context)。例如:

·质量属性需求:当今的Web应用普遍将应用的可扩展性和可用性需求作为早期的技术约束,并在架构中体现出来。这是一个典型的从业务角度受技术环境影响的例子。

·标准遵从性:通常在一些有大量软件管理标准的组织中,特别是在银行、保险和卫生保健等领域,这些标准会被添加到架构的早期约束中。这是一个典型的受外部技术环境影响的例子。

·组织约束:通常对于一些组织,它们对某类架构的使用很有经验,或者具有一组在特定编程环境下操作这种架构的团队(J2EE是一个很好的例子),考虑在这类架构上的投资和要掌握的相关技能,它们倾向于为未来的项目采取相似的架构,从而降低成本,保证生产效率。这是一个典型的内部业务环境的例子。

·专业背景:除了这些外部环境之外,架构师对系统架构的选择,主要是由他的独特经验所决定的。对于一个架构师来说,往往会继续在新项目中使用过去最成功的一组架构。

架构的选择还受个人的教育和职业培训,以及专业同行的影响。1.2.7 架构是对系统的文档化

每个系统都有一个架构,无论它是否被正式地文档化。但是,合理的架构文档可以有效地刻画系统。由于架构捕获了系统的初始要求、约束条件和利益相关者的权衡,所以用文档来正确记录架构是一个很好的做法,该文档可以作为后续培训的基础。它还有助于利益相关者之间持续的沟通,并有助于根据不断变化的需求对架构进行后续迭代更新。

架构文档化的最简单方法是为系统和组织架构的不同方面创建图表,包括组件架构、部署架构、通信架构以及团队或企业架构。

其他一些数据也需要在早期捕获,包括系统需求、约束条件、早期设计决策以及这些决策的依据等。1.2.8 架构通常会遵循某个模式

大多数架构都遵循一定的风格,这些风格在实践中取得了很大的成功,它们被称为架构模式。例如,这类架构模式有客户机-服务器模式、管道和过滤器模式、基于数据的架构模式和其他模式。当架构师选择了一个现有的架构模式时,他将参考甚至重用许多与此模式相关的现有用例和实例。当前可见的架构中,架构师的工作是同时使用多个模式和匹配现成可用的模式集来解决手边问题。

例如,右图是一个客户端-服务器架构的示例。

下图描述了另一种常见的架构模式,即用于处理数据流的管道和过滤器架构。

我们将在本书的后面看到更多的架构模式的例子。1.3 软件架构的重要性

到目前为止,我们已经讨论了软件架构的基本原理,并看到了它的一些特征。显然,这些部分认为软件架构很重要,而且是软件开发过程的关键一步。

现在是唱反调的时候了,回顾一下软件架构,并提出一些存在的问题:

·为什么要用软件架构?

·为什么软件架构很重要?

·为什么不构建没有正式软件架构的系统?

大家要熟悉软件架构提供的一些关键洞察,否则在一个非正式的软件开发过程中很容易忽视它们。在下表中,我们重点关注与系统相关的技术或开发方面的内容。

还有一些与系统的业务环境相关的其他方面,对这些方面架构也都能提供宝贵的指导。然而,由于本书主要是关注软件架构的技术,所以只讨论上表中列出的几个方面。

现在,让我们来谈谈下一个问题:

为什么不构建没有正式软件架构的系统?

如果你已经完全理解了上述洞察,就会不难找到这个问题的答案,它可以总结为以下几句话:

·每个系统都有一个架构,不管它是否有文档记录。

·用正式的文档描述架构,使得它在利益相关者之间共享成为可能,也使得修改管理和迭代开发成为可能。

·当你有了正式的架构定义和文档描述时,就可以利用软件架构的所有其他好处和特征。

·你可能仍然能够在没有正式架构的情况下工作和构建功能系统,但这不会产生可扩展性和可修改性好的系统,并且很有可能使系统的一系列质量属性远远达不到原始要求。1.4 系统架构与企业架构

你可能听说过架构师这个称呼只在很少的实际环境中使用。在软件企业或行业中,以下这些工作中的角色或称号往往代表架构师:

·技术架构师

·安全架构师

·信息架构师

·基础架构师

你也可能听说过系统架构师,或许是企业架构师,更或许是解决方案架构师。有趣的问题是:这些人做什么?

现试着找出这个问题的答案。

企业架构师考虑一个组织或机构的整体业务和组织策略,并应用架构原理和实践来指导该组织或机构通过业务、信息、流程和技术变更需求执行策略。企业架构师通常具有较高的策略关注度和较低的技术关注度。其他架构师角色负责自己的子系统和进程。例如:

·技术架构师:技术架构师关注一个组织或机构使用的核心技术(硬件、软件、网络)。安全架构师创建或调整应用程序使用的安全策略,以适应团队的信息安全目标。信息架构师提出了架构解决方案,以使输入应用程序的信息或者从应用程序中获得的信息可用,从而促进团队的业务目标得以实现。

这些具体的架构角色都关心自己的系统和子系统。因此,这些角色都是系统架构师角色。

这些架构师帮助企业架构师了解他们所负责的每个业务领域的小区域,这有助于企业架构师获得信息来制定业务和组织策略。

·系统架构师:系统架构师通常具有较高的技术关注度和较低的策略关注度。在一些面向服务的软件组织中,一个实际的做法是:有一个解决方案架构师,他将通过集成不同的系统为某个特定的客户端创建一个解决方案。在这种情况下,不同的架构师角色通常被组合成一个,具体取决于组织的规模,以及项目的具体时间和成本要求。

·解决方案架构师:解决方案架构师通常跨越策略与技术关注点、组织与项目范围,处在中间位置。

下图描绘了一个组织或机构中的不同层级——技术、应用程序、数据、人员、流程和业务,并非常清晰地展示了不同架构师角色各自的关注领域:

系统架构师展示在图的左下方,他们查看企业的系统组件。他的重点是企业的应用程序和数据,以及为应用程序提供支持的软硬件堆栈。

另一方面,企业架构师在顶层,由自顶向下的视角来看企业,包括业务目标是什么和都有哪些人员,而不仅仅是为组织或机构提供一个基础系统。业务流程的垂直栈将组织或机构的核心技术组件与它的人员、业务组件连接起来。这些过程由企业架构师与其他利益相关者商议后决定。

现在你已经理解了企业架构和系统架构背后的联系,那么来看一些正式的定义:“企业架构是一个定义企业结构和行为的概念蓝图。它确定了企业结构、流程、人员和信息流动如何与其核心目标相一致,以便有效地实现当前和未来的目标。”“系统架构是系统的基本组织形式,由其结构和行为视图表示。该结构由两部分确定:构成系统的组件和组件的行为。组件的行为是指组件之间的交互,以及组件与外部系统之间的交互。”

企业架构师关注一个组织或机构中的不同元素及其相互作用将如何以有效的方式实现组织或机构的目标。在这项工作中,他不仅需要组织或机构中技术架构师的支持,还需要管理人员的支持,例如项目经理和人力资源等专业人员的支持。

另一方面,系统架构师操心的是核心系统架构如何映射到软件和硬件架构上,以及人员和系统组件之间进行交互的各种细节。他的关注点从来没有超出系统及其各种交互所定义的范围。

下图描述了迄今为止讨论过的各种不同架构师角色的关注点和关注范围:1.5 架构的质量属性

现在让我们关注另一方面,这方面构成了本书其余部分的主要话题——架构的质量属性。

在1.4节中,我们讨论了架构是如何平衡和优化利益相关者的需求,还看到了一些利益相关者的需求之间存在矛盾的例子,架构师需要选择一种平衡的架构来进行调节。

质量属性之前只用于简单地定义架构中需要权衡的一些方面,现在是时候正式定义什么是架构质量属性了:“质量属性是系统的可度量和可测试的特性,可用于评估系统在其指定环境中的非功能性需求方面的达成情况。”

许多方面都符合架构质量属性一般定义的都可以归结为架构质量属性。但是,在本书的其余部分,我们将重点关注以下几个质量属性:

·可修改性

·可测试性

·可扩展性

·性能

·可用性

·安全性

·可部署性1.5.1 可修改性

许多讨论表明,典型软件系统大约80%的成本发生在初始开发和部署之后。这足以表明系统初始架构的可修改性有多么重要。

可修改性可以定义为对系统进行更改的容易程度,以及系统对更改进行调整的灵活性。它是一个重要的质量属性,因为几乎所有的软件系统在它的生命周期上都会发生变化——为了修复问题,增加新特性,提高性能,等等。

从架构师的角度来看,对可修改性的兴趣点如下:

·难点:对系统进行修改的容易程度。

·成本:进行修改需要的时间和资源。

·风险:任何与系统修改相关的风险。

现在,我们讨论的是什么类型的修改呢?是对代码的修改、对部署的修改,还是对整体架构的修改?

回答是:可以是任何层次上的修改。

从架构的角度来看,通常可以在以下3个层面捕获这些修改:

1.本地(local):本地修改仅影响特定元素,可以是一段代码,例如函数、类、模块或配置元素(如XML或JSON文件)。这种类型的修改不会连带影响相邻元素或系统的其余部分。本地修改最易操作且风险最小的,通常可以通过本地单元测试快速验证。

2.非本地(non-local):非本地修改涉及多个元素。举例如下:

·修改数据库方案,需要逐层地修改应用程序代码中表示该方案的模型类。

·在JSON文件中添加新的配置参数,然后为使之生效,需要使用解析器解析使用该参数的文件和/或应用程序。

非本地修改比本地修改更难,它需要仔细分析,并尽量进行集成测试以避免代码回归(code regression)。

3.全局(Global):全局修改涉及从上到下的架构修改,或全局的元素修改,这些修改自顶向下影响软件系统的重要部分。举例如下:

·将系统架构从RESTful修改为基于Web服务的信息交互(SOAP、XML-RPC和其他)。

·将Web应用程序控制器从Django修改为基于Angular-js的组件。

·性能修改需求,需要在前端预加载所有数据,以避免在线新闻应用程序(online news application)的内联模型API调用。

这些修改风险最大,在资源、时间和金钱方面的成本也最高。架构师需要仔细讨论修改可能产生的不同场景,并让团队通过集成测试对不同场景建模。模仿(mock)在此类大规模的修改中可能是非常有用的。

右表展示了系统修改不同层次的成本与风险之间的关系。

在代码级的可修改性也与其可读性直接相关:“代码可读性越强,其可修改性就越强,代码的可修改性与可读性成正比。”

可修改性也与代码的可维护性有关。具有紧密耦合组成元素的代码块所需的修改比具有松散耦合组成元素的代码块所需的修改要少得多。这就是可修改性的耦合(coupling)。

类似地,没有明确定义角色和职责的类或模块比明确定义了职责和功能的要更难修改,这就是软件模块的内聚(cohesion)。

给定一个虚构模块A,下表展示了它的内聚、耦合和可修改性之间的关系,其中耦合反映的是从模块A到另一模块B的耦合。

从右表可看出,高内聚和低耦合的代码块将具有最好的可修改性。

除此之外,还有以下这些影响可修改性的其他因素:

·模块规模(代码行数):规模增大会使可修改性降低。

·开发某个模块的团队成员数:一般来说,考虑到合并和维护统一代码库的复杂性,当很多团队成员参与某一个模块开发时,该模块的可修改性降低。

·模块的外部第三方依赖:外部第三方依赖越多,代码的修改就会越困难。这可以被认为是模块的一种扩展的耦合。

·模块API的错误使用:如果存在其他模块使用某个模块的私有数据时,不是通过该模块提供的公共API(正确的),则修改该模块时难度较大。所以,在你所在的企业内确保遵循模块的使用规范来避免这种情况是很重要的。这可以被认为是紧密耦合的极端情况。1.5.2 可测试性

可测试性是指一个软件系统支持通过测试来检测故障的程度。可测试性也可以认为是一个软件系统向最终用户和集成测试隐藏了多少缺陷——一个系统的可测试性越好,它能隐藏的缺陷越少。

可测试性还与软件系统行为的可预测性有关。可预测性越好,则越能允许更多的重复测试,也越能允许基于输入数据或准则来开发更多的标准测试套件。不可预测的系统对任何类型的测试都不太适用,甚至极端情况下根本不能测试。

在软件测试中,为了控制系统的行为,典型的做法就是向系统发送一组已知输入,然后观察系统在该组输入下的一组已知输出,两者组合形成一个测试用例。测试套件或测试装备通常由很多这样的测试用例组成。

测试断言是对于给定输入,当测试对象的输出与预期输出不匹配时,用于将测试用例判断失效的技术。所有断言通常都是在测试执行阶段的某个特定步骤通过手工添加的编码,以检查测试用例在不同步骤中的数据值。一个简洁的函数f('X')='Y'单元测试用例的代表性流程图如下所示。

上图展示了一个可测试函数“f”的测试流程示例,其中样本输入是“X”,预期输出是“Y”。

为了在失效发生时重建会话(session)或状态,通常使用记录/重放(record/playback)策略,它使用专门的软件(如Selenium),记录导致某一特定失效的所有用户动作,并将其保存为一个测试用例。记录/重放的实现是通过使用相同待测软件、相同的测试用例、相同UI动作和顺序来达到的。

与可修改性类似,可测试性也与代码的复杂性有很大关系。当系统的一部分可以隔离开并独立运行于系统的其余部分时,该系统的可测试性将更高。换句话说,低耦合的系统比高耦合的系统更易于测试。

与早先提到的可预测性有关,可测试性的另一个方面是减少非确定性。在编写测试套件时,需要将要测试的对象与系统中存在行为不可预测倾向的其他部分隔离开,以便已测对象的行为是可预测的。

例如,一个多线程系统,它要响应在系统其他部分引发的事件。整个系统可能是非常难以预测的,不适合重复测试。相反,如果把事件子系统分离开,并尽可能模仿其行为,以便控制这些事件输入,然后接收事件的子系统就会变得可预测,从而可测试。

下图说明了系统的可测试性、可预测性与组件的耦合和内聚之间的关系。1.5.3 可扩展性

现代Web应用都在扩大规模。如果你是任何现代软件组织中的一员,很可能已经听说或正在从事云计算应用方面的工作,这些应用可以按需求弹性扩展。

系统的可扩展性是指系统能够适应不断增长的负载需求,但同时要保证可接受的处理性能。

在软件系统中,可扩展性通常可分为如下两大类:

·横向(水平)可扩展性:横向可扩展性意味着通过向其中添加更多计算节点,以使得软件系统向外或向内扩展。过去十年集群计算的进展催生了一种可在Web上部署的商业化的具有横向可扩展性的弹性系统,即Web服务。一个著名的例子是亚马逊Web服务。通常在横向可扩展的系统中,数据存储与计算是在单元(unit)或节点(node)上进行的。这些单元或节点一般是运行在被称作虚拟专用服务器(VPS)上的虚拟机。为实现“n”次扩展,系统添加n个或更多节点,通常使用一个负载均衡器前端来控制。向外扩展(scaling out)意味着添加更多节点来增大可扩展性,向内扩展(scaling in)意味着删除现有节点来减小可扩展性。一个Web应用程序部署架构的横向扩展示例如下所示。

·纵向(垂直)可扩展性:纵向可扩展性涉及系统单个节点中资源的添加或移除。通常通过在集群中向单个虚拟服务器中添加或移除CPU或RAM(内存)来实现。添加即为扩大规模,删除即为缩小规模。另一种扩大规模的方法是通过加强其处理能力来增加系统中现有软件过程的容量,这一般通过增加应用程序的可用进程或线程数来实现。举例如下:

·通过增加工作进程数来增加Nginx服务器进程的容量。

·通过增加PostgreSQL服务器的最大连接数来增加其容量。1.5.4 性能

系统的性能与其可扩展性有关。系统的性能可以定义如下:

计算机系统的性能是指系统在给定的计算资源内完成的工作量。完成的工作量和计算资源数量的比例(work/unit)越高,性能越高。

度量性能的计算资源单位可以是以下几种之一:

·响应时间:一个函数或执行单元运行所需的时间,根据实际时间(用户时间)和时钟时间(CPU时间)来计算。

·延迟:某个系统被激活并提供响应所需的时间。例如,从终端用户角度来说,一个Web应用程序请求-响应循环完成所需的时间即为延迟。

·吞吐量:系统处理信息的某种比率。具有更高性能的系统通常具有更高的吞吐量,相应地具有更高的可扩展性。例如,一个电子商务网站的吞吐量是每分钟完成交易的数量。

性能与可扩展性密切相关,特别是纵向可扩展性。一个系统在内存管理方面具有出色的性能,就可以通过添加更多RAM来轻松地进行纵向扩展。

类似地,一个具有多线程工作负载特性并对多核CPU优化写入的系统,可通过添加更多CPU内核来扩展。

一般认为,横向可扩展性与其自身计算节点内系统的性能没有直接联系。然而,如果编写的系统不能有效利用网络而产生网络延迟问题,则可能会导致无法进行有效的横向扩展。因为网络延迟所花费的时间将抵消通过分布式工作方式获得的任何可扩展性的好处。

一些动态编程语言(如Python)在纵向扩展时存在固有的可扩展性问题。例如,Python(CPython)的全局解释器锁(GIL)会阻止它充分利用可用的CPU内核进行多线程方式的计算。1.5.5 可用性

可用性是指软件系统可以在需要时进行相关操作的特性。

系统的可用性与其可靠性密切相关,系统越可靠,可用性就越好。

影响可用性的另一个因素是系统从故障中恢复的能力。系统可能非常可靠,但当其子系统全部或部分出现故障时,如果系统无法恢复正常,那么也可能无法保证其可用性。这种情况称作恢复(recovery)。

系统的可用性可以定义如下:“系统的可用性是系统处于完全可操作状态的程度,以便在任何时候获得调用请求时可以执行其功能。”

在数学上,可以用如下公式表示:

Availability=MTBF/(MTBF+MTTR)

上述公式中的术语解释如下:

·MTBF:平均无故障时间(Mean time between failures)。

·MTTR:平均修复时间(Mean time to repair)。

这通常被称为某个系统的任务完成率(mission capable rate)。

可用性技术与恢复技术密切相关,这是由于我们不能期望任何一个系统达到100%的可用性。相反,我们需要针对各种故障以及如何从故障中恢复的策略进行认真筹划,发掘各种故障诊断技术,因为这直接决定了系统可用性。这些技术可以分为以下几类:

·故障检测:检测故障并采取相应措施的能力有助于防止整个系统或系统的某些部分出现完全瘫痪的情况。故障检测通常涉及诸如监控、类心率检查(heartbeat)和ping或者echo消息等步骤。这些步骤被发送到系统的某些节点,并通过检测这些节点的响应(response)来判断它们所处的状态(存活、死亡,还是即将发生故障)。

·故障恢复:检测到故障后,下一步就是考虑如何从故障中恢复系统,并使其处在可用的状态。这里使用的典型策略包括热备份或温备份(即主动冗余、被动冗余策略)、回滚、优雅降级,以及重试(retry)等。

·故障预防:这种方法就是使用主动的故障预测和预防技术,尽量避免系统走到恢复那一步。

CAP定理指出:系统的可用性与其数据的一致性有密切联系。该定理对系统在网络分区情况下的一致性与可用性的权衡进行理论上的限制。CAP定理指出,系统可以在一致性或可用性之间进行选择——通常会导致两种阔型(broad type)的系统,即CP(一致性和对网络故障容忍度)和AP(可用性和对网络故障的容忍度)。

可用性还与系统的可扩展性策略、性能指标及其安全性有关。例如,可高度横向扩展的系统将具有非常高的可用性,因为它允许负载均衡器找出那些不活动的节点并把它们从配置中快速去除。

在试图扩大某个系统的规模之前,可能需要仔细监控其性能指标。即使在系统节点完全可用时,如果软件进程受到CPU时间或内存等系统资源的挤压,系统也可能存在可用性问题。这就是性能测量变得至关重要的原因,同时还需要对系统的负载因素进行监测和优化。

随着Web应用程序和分布式计算的日益普及,安全性也是影响可用性的一个方面。恶意黑客有可能在你的服务器上启动拒绝远程服务的攻击,如果系统不能防范此类攻击,则可能会整体不可用或部分不可用。1.5.6 安全性

软件领域中的安全性可以被定义为系统的一种能力,即避免被未经身份验证的访问损害数据和逻辑,同时继续向通过相应认证的其他系统和角色提供服务。

当有人故意破坏系统以获取非法访问权限,并进一步破坏其服务、复制或修改其数据,或拒绝合法用户的访问,这时安全危机或攻击就发生了。

在当今的软件系统中,用户的各种角色分别独自拥有系统不同部分的权限。例如,一个具有数据库的典型Web应用可以定义以下角色:

·user:系统的终端用户,可在登录后访问其私有数据。

·dbadmin:数据库管理员,可以查看、修改或删除所有数据库数据。

·reports:报告管理员,只对数据库的相应部分和处理报告的代码有管理权限。

·admin:超级用户,具有整个系统的编辑权限。

通过用户角色来分配系统控制权的方法称为访问控制。访问控制将用户角色与特定的系统特权关联起来,从而将实际的用户登录与这些特权的权限授予分离。

这就是一种安全性的授权(authorization)原理。

安全性的另一个方面是交易(transaction),每个人必须验证参与交易的另一个人的实际身份。常用的技术有公钥加密和消息签名等。例如,当你用GPG或PGP密钥签署电子邮件时,你是在为自己做验证——这条信息的发送者是我(A先生),在另一边接收电子邮件的是B先生。这也是一种安全性的授权原理。

安全的其他方面如下:

·完整性(integrity):这种技术用于确保数据或信息在向终端用户传输时不被篡改。例如消息哈希和CRC校验等。

·来源(origin):这种技术用于确保终端接收器接收到的数据的来源与它所声称的来源完全相同。比如SPF、Sender-ID(用于电子邮件)、公钥证书和链(用于使用SSL的网站),以及一些其他方法。

·真实性(authenticity):这种技术将信息的完整性和来源结合在一起。这样可以确保消息的作者不能否认消息的内容及其来源作者(自己)。这通常使用数字证书机制。1.5.7 可部署性

可部署性是质量属性之一,但不是软件的基础属性。本书之所以对这方面感兴趣,是因为它在Python编程语言生态系统的许多方面起着至关重要的作用,并且对编程人员有一定用处。

可部署性是指软件从开发环境到产品运行环境移交的难易程度。它与构建系统的技术环境、模块结构和系统使用的运行时/编程语言有关,更多的是一种功能部署,与系统的实际逻辑或代码无关。

以下是一些与可部署性相关的因素:

·模块结构:如果你的系统将代码组织到良好定义的模块/项目中,将系统划分为易于部署的一个个子单元,则部署会容易得多。另一方面,如果将代码组织成只具有单个设置步骤的单体项目,那么代码将难以部署到多节点群集中。

·产品运行环境与开发环境:与开发环境结构非常相似的产品运行环境会使部署变得容易。当环境相似时,只需少量更改(主要是配置上),开发人员或者开发运维团队就可使用同一组脚本和工具链,与部署到开发服务器上一样,把系统部署到产品运行服务器上。

·开发生态系统(development ecosystem)支持:为系统运行时提供成熟的工具链支持,允许各种依赖关系的自动建立和验证等配置项内容,从而提高可部署性。Python等编程语言在开发生态系统中有着丰富的这方面的支持,同时也有很多可用的工具供开发运维专业人士使用。

·标准化配置:一个好的方案是保持开发者环境的配置结构(文件、数据库表和其他)和产品运行环境的一致。实际的对象或文件名可以不同,但是如果配置结构在两个环境中都有很大差异,那么可部署性就会降低,因为需要的额外做工作来将环境配置映射到相应结构。

·标准化基础设施(infrastructure):众所周知,将部署保持在一个同质或标准化的基础设施上,对可部署性是很有好处的。例如,如果你设定前端应用程序运行在4GB RAM(基于Debian的64位Linux VPS)上,则可以轻松地部署此类节点(使用脚本自动执行,或使用亚马逊这类提供商提供的弹性计算方法),并在开发环境和产品运行环境中保持一套标准的脚本。另一方面,如果你的产品部署环境由不同基础设施组成,例如,混合使用具有不同容量和资源规格的Windows和Linux服务器,则通常需要两倍的工作量来进行部署。

·容器(container)的使用:随着Docker和Vagrant等建立在Linux上的技术的诞生与普及,容器软件的使用已成为在服务器上部署软件的最新趋势。它可以规范你的软件,并减少启动/停止节点所需的开销,从而使部署更容易,因为容器不会带来完整虚拟机的开销。这是一个值得注意的有趣趋势。1.6 本章小结

在本章中,我们了解了软件架构,看到了软件架构的不同方面,并且了解到每个架构都组成一个系统,该系统在其相关的环境中工作。本章还简要介绍了软件架构与软件设计的不同之处。

讨论了软件架构的各种特点。例如,软件架构如何定义一个结构,如何选择一组核心元素,以及如何把相关利益者联系起来等。

还讨论了为什么软件架构对组织机构来说非常重要,以及为什么给软件系统定义一个正式的软件架构是一个好方案等。

接下来讨论了组织机构中不同架构师角色之间的区别。我们看到了系统架构师扮演的各种不同角色,看到了企业架构师的关注点与系统架构师是不同的。还利用插图具体说明了相关策略和技术的广度以及技术的深度等问题。

最后讨论了与本书主题相关的内容:架构质量属性。定义了质量属性是什么;关注了可修改性、可测试性、可扩展性/性能、安全性和可部署性的细节;关于这些属性的细节,讨论了它们的定义、技术以及它们之间的相互关系。

以本章为基础,后面会开始讨论这些质量属性,然后详细讨论使用Python编程语言来实现这些质量属性时会用到的各种技术和技巧。

下一章将从本章讨论的第一个质量属性开始,即可修改性及与其关联的一个属性——可读性。第2章编写可修改可读的代码

本书的第1章首先介绍了软件架构的方方面面,以及相关术语的定义,接着介绍了软件架构在不同层面上需要考虑的问题,最后介绍了在系统构建过程中应当考虑的各种架构质量属性。本书将深入介绍这些架构质量属性及其相关定义,同时讨论在系统构建过程中为了使系统拥有这些属性需要考虑的问题。

从本章开始,本书将依次聚焦各个质量属性,在各章中详细讨论。书中将通过各个质量属性的要素、达到该属性的技巧以及在编程中的注意要点等方面来深入探讨每个质量属性。由于本书的关注点在于Python及其生态系统上,所以本书也使用基于Python的代码和第三方软件系统作为示例,进一步阐述如何达到和维护这些质量属性。

本章聚焦的质量属性是可修改性。2.1 什么是可修改性

架构质量属性中的可修改性可以定义为:

可修改性是指对一个系统进行修改的容易程度,以及系统适应这些修改的灵活性。

第1章曾提到过可修改性的相关概念,例如内聚、耦合等,本章将对可修改性进行深入挖掘,用具体的示例进一步阐述这些概念。不过,在深入进去之前,先来从宏观角度看一看可修改性如何与其他的质量属性相辅相成。2.2 与可修改性相关的几个方面

第1章已经对可修改性的某些方面进行了简单的介绍,下面要对可修改性进行深入阐述,首先来看与可修改性关系密切的几个方面:

1)可读性(readability):可读性可以定义为一个程序的逻辑能够被理解的容易程度。可读的软件代码一定有某种特定的风格,符合使用的编程语言规范,根据编程语言的特点编写且逻辑简单明了。

2)模块化(modularity):模块化是指用封装好的模块构成的软件系统,每个模块只有某个特定的、文档齐全的功能。也就是说,每个模块可以为系统的其他部分提供友好的API。模块化与可重用性紧密相关。

3)可重用性(reusability):可重用性可以用一个软件中的不需修改或经少量修改就能重用到其他系统中的部件数量来衡量,这些部件可以包括代码、工具、设计等。好的系统从构建之初就会考虑到可重用性的问题。可重用性体现在软件开发的DRY原则中。

4)可维护性(maintainability):可维护性是指在保持系统工作效率和有用性的情况下升级系统的容易程度。可维护性是一个集成度量指标,包含可修改性、可读性、模块化和可测试性几个方面。

本章将重点放在可读性和可重用性或模块化上,同样是在Python编程语言的基础上对以上的质量属性依次进行阐述。下面从可读性开始。2.3 理解可读性

软件的可读性与可修改性是紧密联系的。结构规整、文档齐全的代码,同时符合编程语言规范和编程实践规范,具有可读性高、简单简洁、容易阅读和修改等好处。

可读性不仅与代码是否符合编码规范有关,还与代码逻辑的清晰程度、代码利用了多少编程语言的标准风格以及功能的模块化程度等有关。

可以将可读性的要求概括如下:

1)写得好(well-written):一段代码如果用简单的语法,用最鲜明的编程语言风格写成,且逻辑清楚,变量、函数、类和模块等命名明了易懂,就认为编码写得好。也就是说,写得好的代码能准确展现它的功能。

2)文档齐全(well-documented):所谓的文档齐全通常指代码中各行的注释齐全。一段有良好注释的代码能清晰地表达它的功能、输入参数和输出值(如果有的话)以及编写逻辑或算法。注释也可以包含引用的外部库或相关API的用法,以及运行此行代码或此段代码需要的环境配置。

3)结构规整(well-formatted):大多数编程语言,尤其是开源的编程语言,都是由分布在各个不同地理位置但却紧密相连的编程社区通过网络发展起来的,这也决定了编程语言往往都有良好的注释风格规范。一段符合注释风格规范的代码由于结构规整,更易于理解。

没有达到以上要求的代码通常可读性不高。而可读性的缺失会影响可修改性和可维护性,进而要耗费更多的人力或时间资源去维护该系统。2.3.1 Python和可读性

Python是一种从其设计开始就注重可读性的语言。借用一行来自著名的《Zen of Python》书中的句子。

提示:《Zen of Python》中提到有20个原则影响着Python编程语言自身的设计,其中19个已经书面写出了。你可以通过打开Python解释器命令行并输入命令“>>>import this”来查看。

Python作为一种编程语言也很注重可读性,它是通过提供非常简单明了、凝练的关键词来提高可读性的,这些关键词非常契合英文原本的含义;它还提供尽可能少的操作函数和操作符,并奉行下面的理念:

应该有一个——最好只有一个——更简单的方式去实现某个功能。

例如,下面说明了Python中通过序列进行迭代的方法,同时打印索引:

然而,在Python中更常使用enumerate()这个辅助函数去做迭代的工作,该函数可以直接返回序列中所有的索引和对应元素的二元组(idx,item):

在很多其他的编程语言(例如C++、Java或Ruby)中,第一个版本的代码与第二个版本的代码会被认为一样好。但是在Python中,编码时可以使用很多编码惯用法(idiom),它们与Python编程语言的原则一致,即《Zen of Python》。

在这种情况中,Python程序员更喜欢写第二个版本的代码去解决问题,而认为第一个版本与第二个版本相比显得不那么“Pythonic”。

词语“Pythonic”会经常出现在Python社区内的交流中,它是指代码不仅能解决问题,还符合Python社区约定俗成的习惯和惯用法。

注意:“Pythonic”的含义是主观的,但是你也可以认为它就是指符合Python的“Zen”理念的Python代码,或是更通俗些,是使用社区中普遍采用的一些有名的惯用编程实践。

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

下载完整电子书

若在网站上没有找合适的书籍,可联系网站客服获取,各类电子版图书资料皆有。

客服微信:xzh432

登入/注册
卧槽~你还有脸回来
没有账号? 忘记密码?