软件设计
概述
软件架构是一个软件系统的设计图,并不仅限于软件系统的总体架构,还包含一些质量属性以及功能与结构之间的映射关系,即设计决策。
软件架构的两个主要焦点集中于系统的总体架构以及需求和实现之间的对应。
主要思想
主要思想是将注意力集中在系统总体结构的组织上。
思想的实现:
- 运用抽象方法屏蔽错综复杂的模块间连接,使人们的认知提升并保持在整体结构的部件“交互”层次
- 进一步将交互从计算中分离出来,建立“组件+连接件+配置”的软件系统高层结构组织方式
特征
特征:
- 注重可重用性
- 利益相关者较多
- 关注点分离
- 质量驱动
- 概念完整性
- 循环风格
发展阶段
无体系结构->基础研究阶段->核心技术形成->理论体系丰富->理论体系完善及普及应用
软件结构定义
组成派关注于软件本身,将软件架构看作软件和交互的集合
决策派关注于软件架构中的实体(人),将软件架构视为一系列重要设计决策的集合
组成派定义
组成派定义依据:软件架构主要反映系统由哪些部分组成,以及这些部分是如何组成的,强调软件系统的整体架构和配置。
ISO/IEC/IEEE标准:软件架构是某一系统的基本组织结构,其内容包括软件组件、组件间的联系、组件与其环境间的联系,以及指导上述内容设计与演化的原理。
决策派定义
决策派定义依据:软件架构设计是软件设计的一部分,软件设计实际上是开发人员意志和决策在软件开发过程中的体现,软件架构更是高层领导和架构师意志和决策的体现,强调设计决策,更加注重架构风格和模式的选择。
软件架构是一系列重要决策的集合。
参考定义框架
软件架构由组件、连接件、配置、端口和角色组成。
软件架构模型
软件架构建模是对架构设计决策的具象化和文档化。
意义在于它能够将软件架构某些关键或关注的方面剥离出来,使用统一的图形、文档和数据进行描述,达到直观、便捷地理解、分析和交流。
软件架构建模的五种方法
先后出现的五类方法:
- 基于非规范的图形表示的建模方法
- 基于UML的建模方法
- 基于形式化的建模方法
- 基于UML形式化的方法
- 其他建模方法
基于UML的建模方法优点:
- 通用的模型表示法、统一的标准,便于理解和交流
- 支持多视图架构,能够从不同角度来刻画软件架构,可以有效地用于分析、设计和实现过程
- 有效利用模型操作工具,缩短开发周期,提高开发效率
- 统一的交叉引用模型信息的方法,有利于维护开发元素的可处理性,避免错误的产生
缺点:
- 对架构的构造性建模能力不强,缺乏对架构风格和显示连接件的直接支持
- 虽然UML使用交互图、状态图和活动图描述系统行为,但语义的精确性不足
- 使用UML多视图建模产生信息冗余和不一致
- 对架构的建模只能到达非形式化的层次,不能保证软件开发过程的可靠性,不能充分表现软件架构的本质。
软件架构风格
软件架构风格是描述某一特定应用领域中系统组织方式的惯用模式,作为“可复用的组织模式和习语”,为设计人员的交流提供了公共的术语空间,促进了设计复用与代码复用。
架构风格的基本属性:设计元素的词汇表;配置规则;元素组合的语义解释以及使用某种风格构建的系统的相关分析
优势:
- 可以极大地促进设计的重用性和代码的重用性,并且使得系统的组织结构易被理解。
- 使用标准的架构风格可较好地支持系统内部的互操作性以及针对特定风格的分析
数据流风格
数据到达时激活,无数据时不工作
批处理风格
基本组件:独立的应用程序
连接件:某种类型的介质,完整的数据
特点:
- 近乎线性
- 每个处理步骤都是一个独立的程序
- 每一步在前一步结束后才开始
- 数据必须是完整的,以整体的方式传播
管道-过滤器风格
应用场景:数据源源不断地产生,系统需要对这些数据进行若干处理(分析、计算、转换等)。
基本组件:过滤器(功能模块)
连接件:管道(数据流)
过滤器的特点:独立性
- 过滤器独立完成自身功能,相互之间无需进行状态交互。
- 过滤器自身无状态
- 过滤器对其上下游的过滤器“无知”
管道的特点:
- 管道的作用:将数据从一个过滤器的输出口转移到
另一个过滤器的输入口 - 管道是单向流动的。
- 管道可以有缓冲区。
结果的正确性不依赖于各个过滤器运行的先后次序
管道—过滤器风格优点:
- 由于每个组件行为不受其他组件的影响,整个系统的行为易于理解。隐蔽性,高内聚低耦合
- 管道-过滤器风格支持功能模块的复用。
- 基于管道-过滤器风格的系统具有较强的可维护性和可扩展性。
- 支持一些特定的分析,如吞吐量计算和死锁检测等。
- 管道-过滤器风格具有并发性
缺点:
- 管道-过滤器风格往往导致系统处理过程的成批操作。
- 根据实际设计的需要,设计者也需要对数据传输进行特定的处理(如为了防止数据泄漏而采取加密等手段,或者使用了底层公共命名),导致过滤器必须对输入、输出管道中的数据流进行解析或反解析,增加了过滤器具体实现的复杂性,系统性能不高。
- 交互式处理能力弱
调用/返回风格
主程序/子程序风格
该架构风格从功能的观点设计系统◦ 通过逐层分解和逐步细化得到系统架构,即将大系统分解为若干模块(模块化),主程序调用这些模块实现完整的系统功能。主程序的正确性依赖于它所调用的子程序的正确性。
组件为主程序和子程序,连接件为调用-返回机制,拓扑结构为层次化结构。
优点:
- 结构化程序设计的典型风格,相对于非结构化设计逻辑清晰,易理解。
- 开发过程采用逐步细化,将大系统分解为若干模块(模块化)。
缺点:
- 对数据存储格式的变化将会影响几乎所有的模块。
- 结构化程序在规模变大时会难理解、难测试、难维护。
- 这种分解方案难以支持有效的复用。随着程序规模的增大,大量函数、变量之间的关系错综复杂,要抽取可重用的代码往往变得十分困难。
面向对象风格
组件:管理器
连接件:过程调用
约束:去中心化,通常是单线程
优点:
- 对象隐藏了其实现细节,所以可以在不影响其它对象的情况下改变对象的实现,不仅使得对象的使用变得简单、方便,而且具有很高的安全性和可靠性。
- 设计者可将一些数据存取操作的问题分解成一些交互的代理程序的集合。
缺点:
- 大量对象需要额外的结构化。当系统中有大量的对象时,需要额外的结构来组织这些对象,以便更好地管理和理解它们
- 管理大量交互。
- 行为的责任分布使系统难以理解。当行为的责任分布在多个对象和类之间时,系统的逻辑可能会变得复杂,难以理解和维护。例如,一个对象的行为可能依赖于多个其他对象的状态和行为。
层次化风格
基本思想:在分层系统(Layered System)中,系统被组织成若干个层次,每个层次由一系列组件组成;层次之间存在接口,通过接口形成call/return的关系——下层组件向上层组件提供服务,上层组件被看作是下层组件的客户端。
在分层架构中,组件被划分成几个水平层,每个层在应用中执行特定角色。
组件:通常是复合体;复合体通常是由一系列程序组成的
大多数分为四个标准层:表现层,业务层,持久层,数据库层
连接件:取决于组件的结构;经常在受限可见性下调用过程
约束:单线程
特点:
- 分层架构中的每层为上一层提供服务,使用下一层的服务,只能见到与自己邻接的层。
优点:
- 支持基于可增加抽象层的设计,允许将一个复杂问题分解成一个增量步骤序列的实现。
- 支持扩展。每一层的改变最多只影响相邻层。
- 支持重用。只要给相邻层提供相同的接口,它允许系统中同一层的不同实现相互交换使用。
缺点:
- 不是所有系统都容易用这种模式来构建。
- 定义一个合适的抽象层次可能会非常困难,特别是对于标准化的层次模型。
- 层层相调,影响性能
客户机/服务器风格(C/S)
客户机和服务器是两个相互独立的逻辑系统,为了完成特定的任务而形成一种协作关系。
客户机(前端,front-end):业务逻辑、与服务器通讯的接口;
服务器(后端:back-end):与客户机通讯的接口、业务逻辑、数据管理。
两层C/S风格
优点:
- 客户机组件和服务器组件分别运行在不同的计算机上,有利于分布式数据的组织和处理。
- 组件之间的位置是相互透明的
- 客户机程序和服务器程序可运行在不同的操作系统上,便于实现异构环境和多种不同开发技术的融合。
- 软件环境和硬件环境的配置具有极大的灵活性,易于系统功能的扩展。
- 将大规模的业务逻辑分布到多个通过网络连接的低成本的计算机上,降低了系统的整体开销。
缺点:
- 开发成本较高(客户机的软硬件要求高)。
- 客户机程序的设计复杂度大,客户机负荷重。
- 信息内容和形式单一。
- C/S架构升级需要开发人员到现场更新客户机程序,对运行环境进行重新配置,增加了维护费用。
- 两层C/S结构采用了单一的服务器,同时以局域网为中心,难以扩展到Internet。
- 数据安全性不高,客户端程序可以直接访问数据库服务器。
三层C/S风格
相比两层C/S的优点:
- 合理地划分三层结构的功能,可以使系统的逻辑结构更加清晰,提高软件的可维护性和可扩充性。
- 在实现三层C/S架构时,可以更有效地选择运行平台和硬件环境,从而使每一层都具有清晰的逻辑结构、良好的负荷处理能力和较好的开放性。
- 在C/S架构中,可以分别选择合适的编程语言并行开发。
- 系统具有较高的安全性。
但使用时需要注意2个问题:
- 如果各层之间的通信效率不高,即使每一层的硬件配置都很高,系统的整体性能也不会太高。
- 必须慎重考虑三层之间的通信方法、通信频率和传输数据量,这和提高各层的独立性一样也是实现三层C/S架构的关键性问题。
浏览器/服务器风格(B/S)
与三层C/S结构的解决方案相比,B/S架构在客户机上采用了WWW浏览器,将Web服务器作为应用服务器。
B/S架构核心是Web服务器,数据请求、网页生成、数据库访问和应用程序执行全部由Web服务器来完成。
在B/S架构中,系统安装、修改和维护全在服务器端解决,客户端无任何业务逻辑。
优点:
- 客户端只需要安装浏览器,操作简单,能够发布动态信息和静态信息。
- 运用HTTP标准协议和统一客户端软件,能够实现跨平台通信。
- 开发成本比较低,只需要维护Web服务器程序和中心数据库。客户端升级可以通过升级浏览器来实现。
缺点:
- 个性化程度比较低,所有客户端程序的功能都是一样的。
- 客户端数据处理能力比较差,加重了Web服务器的工作负担,影响系统的整体性能。
- 在B/S架构中,数据提交一般以页面为单位,动态交互性不强,不利于在线事物处理
- B/S架构的可扩展性比较差,系统安全性难以保障。
- B/S架构的应用系统查询中心数据库,其速度要远低于C/S架构。
以数据为中心的风格
最初,硬件/软件系统的配置信息均被各自保存在一个配置文件中(.ini);这些文件散落在系统各个角落,很难对其进行维护;引入注册表的思想,将所有.ini文件集中起来,形成共享仓库,为系统运行起到了集中的资源配置管理和控制调度的作用。
注册表中保存了系统的所有硬件和软件配置信息;这些信息影响或控制系统/应用软件的行为,应用软件安装/运行/卸载时对其进行添加/修改/删除信息,以达到改变系统功能和控制软件运行的目的。
仓库风格
基本思想:仓库是存储和维护数据的中心场所
组件:
- 中心数据结构组件,表示当前数据的状态
- 相对独立的组件集合,各个功能模块等
连接件:数据仓库与独立组件之间的交互
优点:
- 便于模块间的数据共享
- 方便模块的添加、更新和删除
- 避免了知识源的不必要的重复存储
缺点:
- 对于各个模块,需要一定的同步/加锁机制保证数据结构的完整性和一致性
黑板系统风格
一个大问题被分解为若干个子问题,每个子问题的解决需要不同的问题表达方式和求解模型,分别设计求解程序
组件:黑板,知识源,控制组件
知识源:
- 知识源是描述某个独立领域问题的知识及其处理方法的知识库
- 知识源分别存放且相互独立
- 他们通过黑板进行通讯 ,合作求出问题的解
- 通常知识源具有“ 条件- 动作”的形式 。当条件满足时 ,知识源被触发 ,其动作部分增加或修改黑板上的内容。
控制器:
- 时刻监视黑板状态变化
- 对黑板上信息的当前状态进行判断和评价
- 当黑板的状态满足了知识源的执行条件时,该知识源被控制器触发并进行计算,然后将结果更新到黑板上
- 这种更新又导致其他知识源参与计算并更新黑板,直到找到问题解为止
优点:
- 便于多客户共享大量数据,他们不关心数据何时有的、谁提供的、怎样提供的。
- 既便于添加新的作为知识源代理的应用程序,也便于扩展共享的黑板数据结构。
- 知识源可重用。
- 支持容错性和健壮性。
缺点:
- 不同的知识源代理对于共享数据结构要达成一致,而且,这也造成对黑板数据结构的修改较为困难——要考虑到各个代理的调用。
- 需要一定的同步/加锁机制保证数据结构的完整性和一致性,增大了系统复杂度。
虚拟机风格
虚拟机是一种软件,创建了一种虚拟的环境,将用户和底层平台隔离开来。
JVM(Java Virtual Machine)可适应所有的硬件与OS平台,从而使得Java具有“一次书写,到处运行”的能力。
在JVM上运行的程序必须首先被编译为标准的二进制格式的文件:.class
Java class文件并不是机器代码或目标代码,而是一种具有标准中间格式的二进制文件,无法直接在任何OS平台上执行;Java class必须在JVM的支持下才能真正执行
解释器风格
基本思想:解释器是一个用来执行其他程序的程序,它针对不同的硬件平台实现了一个虚拟机,将高抽象层次的程序翻译为低抽象层次所能理解的指令,以弥合程序语义所期望的与硬件提供的计算引擎之间的差距。
优点:
- 它有利于实现程序的可移植性和语言的跨平台能力;
- 可以对未来的硬件进行模拟和仿真,能够降低测试所带来的复杂性和昂贵花费
缺点:
- 额外的间接层次导致了系统性能的下降
编译器不会执行输入的源程序代码,而是将其翻译为另一种语言,通常是可执行的机器码或目标码,并输出到文件中以便随后链接为可执行文件并加以执行
在解释器中,程序源代码被解释器直接加以执行
两者的区别:
- 解释器的执行速度要慢于编译器产生的目标代码的执行速度,但是却低于编译器“编译+链接+执行”的总时间
- 解析器执行速度之所以慢,是因为每次解释执行的时候,都需要分析程序的结构,而编译代码则直接执行而无需重复编译
- 解释器对内存的分配是在解释时才进行的;而编译器则是在编译时就规划好了变量的内存使用方案,因此运行时直接将程序代码装入内存并执行即可
解释器有三种策略,分别是传统解释器,基于字节码的解释器和JIT编译器
传统解释器是直接读取源代码并加以执行。
字节码解释器是首先将源代码“编译”为高度压缩和优化的字节码,但并不是真正的机器目标代码,因而与硬件平台无关;编译后得到的字节码然后被解释器加以解释
实时编译器(JIT),字节码在运行时被编译为本机的目标代码。第一步编译得到字节码,然后,字节码被配置到目标系统中,当字节码被执行时,运行环境下的编译器将其翻译为本地机器码
基于规则的系统风格
任何规则都包含两部分:
- IF部分:规则的前提或条件
- THEN部分:规则的结论或触发的行为
一个规则可以有多个条件,使用AND或OR连接
优点:
- 降低了修改业务逻辑的成本
- 缩短了开发时间
- 将规则外部化,可在多个应用之间共享
- 对规则的改变将非常迅速并且具有较低的风险
独立组件风格
独立构件风格的软件系统由多个独立的组件构成,这些组件之间不共享状态,通过网络、消息队列或者其他形式的消息传递机制来进行通信和数据交换。
独立性:
- 组件拥有自己的内部状态和行为,组件间松耦合。
- 组件可以单独测试,单独部署。
- 组件可在不同的物理节点或进程中运行。
组件:
- 功能独立
- 为每个组件定义清晰的接口,包括输入、输出和预期行为。
连接件:
- 消息传递、事件驱动或远程过程调用(RPC)。
- 可使用中间件技术(如消息队列)来协调组件间的交互。
约束:
- 系统执行的过程依赖于被触发事件的上下文约束。
进程通讯:组件作为独立进程运行,通过显式通信协议直接交换数据。消息传递的方式可以是点到点、异步或同步方式及远程过程调用等。
事件驱动:组件通过发布/订阅事件隐式交互,发送者不依赖接收者的存在或响应。
进程通讯风格
进程通讯特点:
- 同步/异步通信
- 点对点交互:组件需明确知道通信对象
- 典型技术:REST API、gRPC、RabbitMQ
进程通讯应用场景:微服务架构、分布式计算
事件驱动风格
消息:
- 计算机中,消息是具有特定含义的数据
- 对象通过发送消息的方式请求另一个对象为其服务
事件:
- 能够激活对象功能的动作。当发生这种动作后将给所涉及对象发送一个消息,对象便可执行相应的功能
通常,在一个系统中,组件接口提供了访问过程或函数的端口的集合,组件通过显式地调用这些过程或函数来与其他组件交互。然而,一种基于隐式调用(implicit invocation)的集成技术非常受关注,该技术就是事件驱动(Event-based) 的软件架构风格。
基本思想:
- 组件不直接调用一个过程,而是发布或广播一个或多个事件。
- 系统中的其它组件通过注册与一个事件关联起来的过程,来表示对某一个事件感兴趣。当这个事件发生时,系统本身会调用所有注册了这个事件的过程。这样一个事件的激发会导致其它模块中过程的隐式调用。
特点:事件的触发者并不知道哪些构件会被这些事件影响,相互保持独立。
事件调度策略:
事件分派模块的功能:负责接收到来的事件并派遣它们到其它模块
- 广播式:派遣模块将事件广播到所有的模块,但只有感兴趣的模块才去取事件并触发自身的行为。
- 选择广播式:派遣模块将事件送到那些已经注册了的模块中。
事件驱动编程的一般步骤:
1 确定响应事件的元素;
2 为指定元素确定需要响应的事件类型;
3 为该元素的相应事件编写事件处理程序;
4 将事件处理程序绑定到指定元素的指定事件。
优点:
- 事件声明者不需要知道哪些组件会影响事件,组件之间关联较弱。一个组件出错将不会影响其他构件。
- 提高软件复用能力。只要在系统事件中注册组件的过程,就可以将该组件集成到系统中。
- 系统便于升级。只要组件名和事件中所注册的过程名保持不变,原有组件就可以被新组件替代。
缺点:
- 组件放弃了对计算的控制权,完全由系统来决定。
- 存在数据交换问题。
- 该风格中,正确性验证成为一个问题。
其他软件架构风格
C2风格
C2风格的主要思想来源于Chiron-1用户界面系统,因此又被命名为Chiron-2,简称C2。
C2架构风格可以概括为:通过连接件绑定在一起的按照一组规则运作的并行组件网络。该规则规定了所有组件之间的交互必须通过异步消息机制来实现
C2是一种基于组件和消息的架构风格,适用于GUI软件开发,构建灵活和可扩展的应用系统。
C2风格的系统组织规则:
- 组件之间不能直接连接
- 组件和连接件都有一个顶部和一个底部
- 组件的顶部应连接到某连接件的底部,组件的底部则应连接到某连接件的顶部
- 一个连接件可以和任意数目的其他组件和连接件连接
- 当两个连接件进行直接连接时,必须由其中一个的底部到另一个的顶部
C2构件的内部,通信和处理是分开完成的
C2连接件:
- 连接件负责把构件绑定在一起,其上可以连接任何数量的构件和连接件
- 连接件的主要职责:
- 消息的路由和广播
- 消息的过滤
优点:
- 可使用任何编程语言开发组件,组件重用和替换易实现
- 由于组件之间相对独立,依赖性小,因而该风格具有一定扩展能力,可支持不同粒度的组件
- 组件不需共享地址空间
- 可实现多个用户和多个系统之间的交互
- 可实现多个工具集和多种媒体类型,动态更新系统框架结构
缺点:
- 不太适合大规模流式风格系统,以及对数据库使用比较频繁的使用
平台/插件风格
插件是一种遵循统一的预定义接口规约编写出来的程序,应用程序在运行时通过接口规约对插件进行调用,以扩展应用程序的功能。
插件的本质在于不修改程序主体(或者程序运行平台)的情况下对软件功能进行扩展与加强。
这意味着软件开发者可以通过公布插件的预定义接口规约,从而允许第三方的软件开发者通过开发插件对软件的功能进行扩展,而无须对整个程序代码进行重新编译。
“平台/插件”软件结构将待开发的目标软件分为两部分:
- 程序的主体或主框架,可定义为平台
- 功能扩展或补充模块,可定义为插件
平台所完成的功能应为一个软件系统的核心和基础,可以把平台基本功能分为两个部分:
- 内核功能:是整个软件的重要功能,一个软件的大部分功能应由内核功能完成。
- 插件处理功能:用于扩展平台和管理插件,为插件操纵平台和与插件通信提供标准平台扩展接口。
插件受到的约束:
- 插件必须能在运行过程中动态地插入平台和从平台中注销,且不影响系统的运行。
- 当在系统中插入插件后,系统的功能得到扩展或升级。
- 多个插件之间、插件和平台之间不会发生冲突。
实现该风格需要定义两个标准接口:
- 平台扩展接口:完全由平台实现,插件只是调用和使用
- 插件接口:完全由插件实现,平台也只是调用和使用。
优点:
- 降低系统各模块之间的互依赖性
- 系统模块独立开发、部署、维护
- 根据需求动态的组装、分离系统
缺点:
- 插件是别人开发的可以用到某主程序中的,只服务于该主程序,可重用性差
面向Agent风格
Agent组件:能够自主运行、具有某种程度的智能以适应环境变化、可以与其他Agent进行交互和通信,并且通常具备一定的目标追求能力。
Agent组件有别于以往任何系统的组件类型,其所具有的自主性、智能性、交互性等特性是传统架构对象所不具备的。
这些Agent可以是物理实体(例如机器人)或软件实体(例如在计算机网络上执行特定任务的程序)。它们通过感知周围环境,采取行动来影响该环境,并基于自身的目标和知识做出决策。
Agent连接件:对复合型组件的连接,该连接能够提供通信、协调、转换、接通等服务
多Agent系统中的连接件并非显示地将两个不同的组件联系起来。不同Agent之间的联系是根据运行时状态来决定的。
优点:
- 面向Agent的软件工程方法对于解决复杂问题是一种好的技术, 特别是对于分布开放异构的软件环境
缺点:
- 通信开销大,多Agent频繁交互可能导致延迟。
- 为了适应动态环境,应对复杂场景,Agent的设计比较复杂。
适用于需要处理高度动态和不确定性环境的应用场景,如自动化、电子商务、信息管理、分布式控制系统等。
面向方面软件架构风格
面向方面的编程(AOP:Aspect Oriented Programming)
系统的有些特性和需求是横切于系统的每一个层面中,并融于系统的每一个组件中,这种特性称为系统的方面(Aspect)需求特性或关注点
应用AOP的主要目的----尽量分离“技术问题实现”和“业务问题实现”:
-
它允许开发者能够对横切关注点进行模块化设计。
-
能够实现分散关注,将通用需求功能从不相关类之中分离出来。
-
能够实现代码重用。
-
方面(Aspect):方面是横切关注点的模块化单元。一个方面可以包含与特定行为相关的建议(advice)、连接点(join points)以及切入点(pointcuts)。
-
建议(Advice):指的是方面应该执行的具体动作。这可以是在特定事件发生之前(前置建议)、之后(后置建议),或者当异常抛出时执行的动作。
-
连接点(Join Point):程序执行过程中的某些点,如方法调用、异常抛出等,在这些点上可以插入建议。
-
切入点(Pointcut):一组连接点的集合,定义了在哪些建议应该被执行的连接点上应用。
-
织入(Weaving):将方面代码整合到主程序逻辑的过程。织入可以在编译期、加载期或运行时完成。
优点:
- 增强功能独立性:通过分离横切关注点,使得系统更加功能独立,每个模块只需专注于其核心职责。
- 减少代码重复:避免了为实现相同的横切功能而在多个地方编写类似的代码。
- 提高可维护性:由于横切关注点被集中处理,因此更容易进行更新和维护。
- 简化设计:让开发者能够更清晰地表达系统的结构和行为。
缺点:
- 有一定学习成本
- 因为建议(Advice)可以改变程序流而不需要直接修改源代码,所以可能会增加调试难度。
- 存在潜在的性能开销,特别是在运行时织入方面时,可能会引入额外的性能成本
面向服务架构风格
SOA 是一个组件模型,它将应用程序的不同功能单元(服务)通过这些服务之间定义良好的接口和契约联系起来。
- 服务请求者:可以是服务或者第三方的用户,通过查询服务提供者在服务注册中心发布的服务接口的描述,通过服务接口描述来通过RPC或者SOAP进行绑定调用服务提供者所提供的业务或服务。
- 服务提供者:作为服务管理者和创建者,必须将服务描述的接口发布到服务注册中心才能被潜在的服务请求发现,能够为合适的服务请求者提供服务 。
- 服务注册中心:相当于服务接口的管理中心,服务请求者者能够通过查询服务注册中心的数据库来找到需要的服务调用方式和接口描述信息。
- 发布:为了便于服务请求者发现,服务提供者将对服务接口的描述信息发布到服务注册中心上。
- 发现:服务请求者通过查询服务注册中心的数据库来找到需要的服务,服务注册中心能够通过服务的描述对服务进行分类,使服务提供者更快定位所需要的服务范围。
- 绑定和调用:服务请求者在查询到所需要服务描述信息,根据这些信息服务请求者能够调用服务。
优点:
- 灵活性,根据需求变化,重新编排服务。
- 对IT资产的复用。
- 使企业的信息化建设真正以业务为核心。业务人员根据需求编排服务,而不必考虑技术细节。
缺点:
- 服务的划分很困难。
- 服务的编排是否得当。
- 如果选择的接口标准有问题,如主流的Web service之类,会带来系统的额外开销和不稳定性。
- 对IT硬件资产还谈不上复用。
- 目前主流实现方式接口很多,很难统一。
- 目前主流实现方式只局限于不带界面的服务的共享。
微服务架构
特点:
- 服务自治
- 微服务围绕具体业务能力组织系统,高度内聚:
- 每个服务独立开发、部署、扩展,技术栈可异构(如Java、Python、Go)
- 各服务拥有专属数据存储
- 轻量级通信机制
- 同步通信:HTTP/REST、gRPC。
- 异步通信:消息队列(如Kafka、RabbitMQ)
- 去中心化治理:
- 服务独立演进,允许团队自治。
- 基础设施通过统一平台管理。
- 容错和弹性设计
- 通过熔断(Hystrix)、重试、降级等机制保障系统可用性和稳定性。
优点:
- 加速开发周期:团队可以并行工作于不同的服务上,加快了新功能的发布速度。
- 技术多样性:可以根据每个服务的需求选择最适合的技术栈。
- 弹性扩展:可以针对具体的服务进行扩展,而不需要扩展整个应用。
- 容错,易维护:单个服务宕机不会导致系统崩溃,且由于服务小并单一职责,更容易理解和维护。
缺点:
- 分布式系统的复杂性
- 运维成本高
- 团队协作需要严格规范接口定义
基于层次消息总线的架构风格(JB/HMB风格)
JB/HMB风格基于层次消息总线、支持组件的分布和并发,组件之间通过消息总线进行通讯。
消息总线是系统的连接件,负责消息的分派、传递和过滤以及处理结果的返回。各个组件挂接在消息总线上,向总线登记感兴趣的消息类型。不要求各个构件具有相同的地址空间或局限在同台机器上,可较好地描述分布式并发系统。
优点:
- 模块化与可维护性
- 灵活、可扩展
- 支持异步通信
- 增强了系统的容错能力
缺点:
- 消息总线本身比较复杂
- 调试和问题定位复杂
- 性能开销大
- 如何保证消息传递的一致性和可靠性
正交架构风格
正交软件架构(Orthogonal Software Architecture) 由组织层(Layer)和线索(Thread)的组件(Component)构成。
其基本思想是把应用系统的结构按功能的正交相关性,垂直分割为若干个线索(子系统),线索又分为几个层次,每个线索由多个具有不同层次功能和不同抽象级别的组件构成。
特点:
- 由完成不同功能的n(n>1)个线索(子系统)组成;
- 系统具有m(m >1)个不同抽象级别的层;
- 线索之间是相互独立的(正交的);
- 系统有一个公共驱动层(一般为最高层)和公共数据结构(一般为最低层)。
优点:
- 结构清晰,易于理解。
- 易修改,可维护性强。
- 可移植性强,重用粒度大。
缺点:
- 在实际应用中,并不是所有软件系统都能完全正交化,或者有时完全正交化的成本太高。因此,在进行应用项目的软件架构设计时,必须反复权衡进一步正交化的额外开销与所得到的更好的性能之间的关系。
异构风格
异构架构是几种风格的组合。
优点:
- 选择异构架构风格,可以实现遗留代码的重用。
- 在某一单位中,规定了共享软件包和某些标准,但仍会存在解释和表示习惯上的不同。选择异构架构风格,可以解决这一问题。
缺点:
- 不同风格之间的兼容问题有时很难解决
模型-视图-控制器风格(MVC)
MVC结构主要包括模型、视图和控制器三部分:
- 模型:模型是应用程序的核心,它封装了问题的核心数据、逻辑关系和计算功能,提供了处理问题的操作过程。
- 视图:视图是模型的表示,提供了交互界面,为用户显示模型信息。
- 控制器:控制器负责处理用户与系统之间的交互,为用户提供操作系统。
优点:
- 多个视图与一个模型相对应。变化——传播机制确保了所有相关视图都能够及时地获取模型变化信息,从而使所有视图和控制器同步,便于维护。
- 具有良好的移植性。由于模型独立于视图,因此可以方便的实现不同部分的移植。
- 系统被分割为三个独立的部分,当功能发生变化时,改变其中的一个部分就能满足要求。
缺点:
- 增加了系统设计和运行复杂性。
- 视图与控制器连接过于紧密,妨碍了二者的独立重用。
- 视图访问模型的效率比较低。由于模型具有不同的操作接口,因此视图需要多次访问模型才能获得足够的数据。
- 频繁访问未变化的数据,也将降低系统的性能。
软件架构解耦
耦合度表示模块之间关系的紧密关系,“低耦合度”是软件设计的目标。
低耦合模块(类或子程序)之间的关系尽可能简单,彼此之间的相互依赖性小,也就是“松散耦合”。
解耦就是降低模块之间的耦合度,也就是尽可能使得模块之间的耦合是松散耦合。
解耦能够保持组件之间的自主和独立性。它的直接结果就是改动成本低,维护成本低,可读性高。
微观角度解耦:选择合理的设计模式进行面向对象程序设计的解耦。
宏观角度解耦:一个结构混乱、系统组件没有内聚、掺杂大量没有必要的耦合、松弛而模糊的软件架构,将导致每个代码组件编写得不好,还会导致重复的代码和工作。从架构层面,基于架构风格,从架构风格(或模式)本身的角度去分析其解耦。
微内核架构及其解耦应用
微内核架构模式也被称为插件架构模式。
应用逻辑被分割为独立的插件模块和核心系统,可以提供可扩展的,灵活的,特性隔离的功能。
微内核包含两个组件:核心系统,插件模块。
微服务架构及其解耦应用
微服务架构是一种将单个应用程序开发为一组小型服务的方法,每个小型服务都在自己的进程中运行,并与轻量级机制(通常是HTTP资源API)进行通信。
这些服务围绕业务功能构建,可通过全自动部署机制独立部署。
这些服务的集中式管理的最小限度是最小的,其可以用不同的编程语言编写并且使用不同的数据存储技术。
提倡将单块架构的应用划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。
软件架构与敏捷开发
敏捷开发的基本理念:
- 强调个体和互动比强调过程和工具更好
- 强调获得可运行的软件比强调完成详尽的文档好
- 强调与客户合作比强调进行详细的合同谈判好
- 强调变化比强调遵循既定的计划好
敏捷开发在实践中表现为一种迭代、增量和持续集成的开发方法。
- 迭代反映了项目的开发节奏,是一个多周期的开发过程。
- 增量说明了项目的实际进展,整个项目就是由很多增量构成的。
- 持续集成反映了集成增量的过程是持续进行的。
软件架构与敏捷开发都是一个权衡的过程:软件架构设计需要权衡涉众们的各种需求,在众多的解决方案中确定唯一的架构设计方案,从而保证软件开发的有效性。敏捷开发是在软件开发过程混沌和大量开发管理活动加入的两个极端中做出的一种权衡。
软件架构与敏捷开发目的都是为了提高软件开发效率、提高软件质量、降低软件成本,将开发团队的价值最大化。
在敏捷开发的支持者看来,传统的软件架构设计与敏捷思想相违,因为过多的预先设计使得软件开发过程在面对变化时缺乏灵活性,其管理成本极有可能由于后续的重构而成为无用功。但是根据学术界与企业界的研究和调查,发现软件架构设计对于敏捷开发来说也是必要的。两者在软件开发实践中能够共同存在,且互相促进。
敏捷开发非常重视软件的架构设计,但是轻架构的详细设计。
- 敏捷思想中将传统的架构设计分成:种子架构设计+详细架构设计。
- 种子架构设计关注软件系统的骨架或轮廓的设计。
- 敏捷开发将详细架构设计转移到Code编码阶段、重构阶段、单元测试阶段等。
- 分离后,敏捷软件种子架构的内容包括:软件的架构层次,重要模块、重要类的说明(无须设计全部的类和方法)等。
敏捷开发把传统软件开发前期的详细架构设计,分散到了整个敏捷开发软件过程中,以达到提高效率、减少风险的目的。
需求分析:
- 敏捷开发中的需求分析引入了架构设计的理念,分为初始阶段需求分析和迭代阶段需求分析。
- 初始阶段的需求分析中摒弃了具体的细节,仅仅抓住软件最高层的概念。
- 迭代阶段需求分析是随着项目的进展逐步完善的,具有高适应性。
初始设计:
- 初始阶段的目标是在所有涉众之间达成关于项目的生命周期目标的协议,在项目进行之前确定重要的业务和需求风险。
- 初始设计需要对软件系统的设计进行全局抽象层次上的考虑。
- 包括系统的基本处理流程、系统的组织结构、模块划分、功能分配等
迭代过程:
- 针对需求的不可预见性,在敏捷开发中使用了迭代过程。
- 长期的计划通常是不稳定的,单次迭代的短期计划是稳定的。
- 迭代开发都是基于上次迭代的结果,每次迭代都有一个坚实的基础。
- 迭代设计:是根据当前迭代过程需要完成的工作任务来进行需求分析、设计和编码等。
- 重构:迭代过程中的重构往往发生在编码阶段,重构是对软件架构的持续改进。
- 确定架构:迭代过程中生产出的软件(*(或组件)经过测试后确实能达到预期的要求,生产出了可交付的软件产品。
- 客户交流:根据交付的可用软件,与客户进行充分交流。通过客户反馈的信息,快速有效地适应变化,在后续的迭代过程中完成客户的新要求。
敏捷的思想在软件架构设计中最主要的体现就是团队设计和简单设计这两种设计理念。
- 团队设计
- 团队设计的理论依据是群体决策,这样可以避免理论上完美,但程序员无法实现的架构设计。
- 方式:
- 全体人员参与架构设计
- 组织优秀的开发人员组成设计组
- 这样设计出来的架构称为原始架构,在后续的迭代过程中不断地反馈和改进。
- 优点:
- 其结论要比个人决策更加完整,避免个人遗漏,相对稳定、周密。
- 缺点:
- 需要额外付出沟通成本、决策效率低、责任不明确等。
- 简单设计
- 敏捷的思想要求软件架构设计必须是简单设计。
- 这里的简单体现在两个方面:表达方式的简单化和现实抽象的简单化。
- 表达方式的简单化:指的是敏捷开发中对详细架构描述文档等中间产物的弱化,只满足有效沟通即可。
- 现实抽象的简单化:指的是仅针对当前需求建模分析,不做“多余的”工作。
- 简单设计可以降低开发成本、提升沟通效率、增强适应性和稳定性。
优秀的敏捷软件架构的设计过程一般同时包含规划式设计和演进式设计,具体体现为初始阶段设计和迭代过程中的设计。
各种敏捷开发方法在实际应用中基本上都会在正式编码前有一个初步的设计。不同方法的初始阶段设计大同小异,都是为了得到一个原始架构,但输出不同:
- XP初始阶段输出的原始架构是以系统隐喻的方式存在的。
- Scrum初始阶段输出的原始架构是以产品功能列表的方式存在的。
- FDD(特征驱动的软件开发)初始阶段输出的原始架构是一个特征表。
XP中的迭代过程
Scrum中的迭代过程
FDD中的迭代过程
小结
架构设计与针对系统做出的关键决策有关,是项目相关人员对系统内部结构和开发方式达成的共同认识。
软件的架构设计是没有确定答案的,面对复杂的业务场景,丰富的框架方案,架构师需要凭经验和直觉进行选择。
一般说来,架构师要根据企业当前人力条件和业务约束进行对资源的合理整合,进行架构设计。
一个优秀成熟的架构应该有优秀的适应性并支持敏捷的思想,及时地适应需求变化并对架构做出适当的调整。
一个优秀的架构,其价值也许并不会直接体现在商业价值上,但它可以减少实现商业价值所需的成本。
软件架构设计和实现
软件架构是软件系统质量的核心。
成功的软件架构应具有以下品质:
- 良好的模块化
- 适应功能需求的变化,适应技术的变化
- 对系统的动态运行有良好的规划
- 对数据的良好规划
- 明确、灵活的部署规划
从需求分析到架构设计
需求分析和软件架构设计是软件全生命周期中两个关键活动。
需求分析的目的是得到一个正确、一致并且无二义的需求规约,作为后续开发、验证及系统演化的基础。
软架构设计是一个架构的定义、文档编写、维护、改进和验证正确实现的活动。其目的是将系统的高层结构显式地表达出来,在较高的抽象层次上为后续的开发活动提供“蓝图”。
需求到软件架构的映射是一个既复杂又细致的工作,对相同的需求采用不同的映射机制会得到不同风格的架构。
从软件需求到软件架构存在的难点:
- 软件需求是频繁获取的非正规的自然语言,而软件架构规约常常是一种正式的语言。
- 系统属性中描述的非功能性需求通常很难在架构模型中形成规约。
- 需要以迭代和同步演化的方式进行软件需求理解和软件架构开发。
- 在从软件需求映射到软件架构的过程中,保持一致性和可追溯性很难,且复杂程度很高。
- 大规模系统必须满足数以千计的需求,从而导致很难确定和细化包含这些需求的架构相关信息。
- 软件需求和软件架构需要满足不同的利益相关者的需求,很难在这些不同利益中找正确的平衡点。
基于体系结构的软件设计(architecture-basedsoftware design, ABSD)方法为软件系统的概念体系结构提供构造方法,概念体系结构描述了系统的主要设计元素及其关系。
ABSD方法有三个基础:
- 功能分解:在功能分解中,ABSD方法使用已有的基于模块的内聚和耦合技术;
- 通过选择体系结构风格来实现质量和业务需求。
- 软件模板的使用:利用一些软件系统的结构。
软件模板是一个特殊类型的软件元素,包括描述所有这种类型的元素在共享服务和底层构造的基础上如何交互。
软件模板还包括属于这种类型的所有元素的功能,这些功能的例子有:每个元素必须记录某些重大事件,每个元素必须为运行期间的外部诊断提供测试点。
ABSD方法的目的是组织最早的设计策略,不包括形成实际的软件构件和类。但要作出有关功能划分和达到不同质量属性机制的决策。
ABSD方法是一个递归细化的方法,软件系统的体系结构通过该方法得到细化,直到能产生软件构件和类。
ABSD方法在生命周期中的位置
ABSD方法的输入由下列部分组成:
- 抽象功能需求:功能需求的抽象描述,及其粗略变化
- 用例:用户与系统之间交互的具体表述,仅考虑重要的用例
- 抽象的质量和商业需求:质量需求尽量具体化
- 质量因素:实际质量和商业需求。采用质量场景可以对质量需求进行特定扩充,使质量因素具体化
- 体系结构选项:对于每个质量和业务需求,都要例举出能满足该需求的所有可能的体系结构
- 约束:前置的设计决策。例如:必须考虑某遗留系统特征
架构需求
一个设计元素有一组功能,这些功能必须分组。分解的目的是使每个组在体系结构内代表独立的元素。分解可以进一步细化。
功能的分组可选择几个标准:
- 功能内聚
- 数据或计算行为的类似模式
- 类似的抽象级别
- 功能的局部性。公共功能区分出来。
架构设计
- 提出结构模型:选择体系结构风格
- 映射构件:为风格分配功能
- 细化模板,分析构件间关联
- 功能校验,设计评审
架构文档化
架构文档化过程的主要输出是架构需求规格说明和测试架构需求的质量设计说明书这两个文档。
文档要从使用者的角度进行编写,必须分发给所有与系统有关的开发人员,且必须保证开发者手上的文档是最新的。
架构复审
架构设计、文档化和复审是一个迭代过程。
从这个方面来说,在一个主版本的软件架构分析之后,要安排一次由外部人员(用户代表和领域专家)参加的复审。
复审的目的是标识潜在的风险,以及尽早发现架构设计中的缺陷和错误,包括架构能否满足需求、质量需求是否在设计中得到体现、层次是否清晰、构件的划分是否合理、文档表达是否明确、构件的设计是否满足功能与性能的要求,等等。
架构实现
架构演化
在构件开发过程中,最终用户的需求可能还有变动。哪怕在软件开发完毕,正常运行后,由一个单位移植到另一个单位,需求也会发生变化。在这两种情况下,就必须相应地修改软件架构,以适应新的软件需求。
需求与架构的协同演化
软件需求和软件架构两者是相辅相成的关系,一方面软件需求影响软件架构设计,另一方面软件架构帮助需求分析的明确和细化。
需求与架构的互相影响可以看成一个螺旋的过程,也是一个双峰的过程。
双峰模型强调软件需求和软件架构的平等性,它是简化版的螺旋模型,在发展需求和架构规约同时,继续从解决方案的结构和规约中分离问题的结构和规约,在一个反复的过程中,产生更详细的需求规约和设计规约,最终把交织在软件开发过程中的设计规约和需求规约分离开来。
从软件架构到详细设计
软件架构设计应当解决的是全局性的、涉及不同“局部”之间交互的设计问题,一般由软件架构师负责,而不同“局部”的设计由后续的详细设计人员负责。
详细设计对软件架构的影响
详细设计主要集中在架构表达式的细化,选择详细的数据结构和算法。
从软件架构映射详细设计
经常出现的问题:
- 缺少重要架构视图,片面强调功能需求。
- 不够深入,架构设计方案过于笼统,基本还停留在概念性架构的层面,没有提供明确的技术蓝图。
- 名不副实的分层架构,缺失层次之间的交互接口和交互机制,只进行职责划分。
- 在某些方面过度设计。
可能的解决方法有:
- 对于缺失重要架构视图问题,可以针对遗漏的架构视图进行设计。
- 对于不够深入问题,需要将设计决策细化到和技术相关的层面。
- 对于名不副实的分层架构问题,需要步步深入,明确各层之间的交互接口和交互机制。
- 虽然我们必须考虑到系统的扩展性,可维护性等,但切忌过度设计。
从架构描述直接到语言的映射方法:
目前的解决方案,或者将高层软件架构模型直接映射成为程序代码,或者经过一系列中间模型的转换,渐进地映射到程序代码。
从ADL(架构描述语言)向程序代码的映射需要考虑:建立从软件架构建模元素到目标语言元素的映射关系、确保映射过程中的语义正确性、提供自动化的转换工具或环境等问题。
通过直接映射将ADL转化成为程序语言往往只能生成较为简单的程序代码框架。
基于模型驱动软件架构(MDA)的映射方法:
MDA区分了三类模型:
- **计算无关模型(CIM)**也称业务模型,描述系统的外部行为和运行环境
- **平台无关模型(PIM)**具有高抽象层次、无关于任何实现技术的模型
- **平台特定模型(PSM)**为某种特定实现技术量身定做,让你用这种技术中可用的实现构造来描述系统的模型。PIM会被变换成一个或多个PSM。
MDA开发步骤:
- 用计算无关模型CIM捕获需求
- 创建平台无关模型PIM
- 将PIM转化成为一个或多个平台特定模型PSM,并加入平台特定的规则和代码
- 将PSM转化为代码等
MDA的根本思想:
将软件系统分成模型和实现两部分:模型是对系统的描述,实现是利用特定技术在特定平台或环境中对模型的解释。模型仅仅负责对系统的描述,与实现技术无关。这是模型的实现技术无关性。
好处:
将模型与实现分离后,能够很好的适应技术易变性。由于实现往往高度依赖特定技术和特定平台,当技术发生迁移时,只需针对这种技术作相应的实现,编写相应的运行平台或变换工具。所以,能够比较好的应对实现技术发展带来的挑战。
软件架构视图(了解)
为了更好地发挥软件架构在系统实现阶段的指导与交流作用,研究者们提出了若干针对实现阶段的软件架构视图:
- 4+1视图:逻辑视图、开始视图、进程视图、物理视图、场景视图
- 四种视图:概念视图、模块视图、执行视图、代码视图
架构设计原则
软件架构设计原则有一般设计原则和关键设计原则两类。
一般原则包含商业原则、数据原则、应用程序原则、技术原则等。
关键设计原则包括关注分离点、单一职责原则、最少知识原则等。
基本原则:
- 商业原则
- 企业利益最大化
- 信息管理,人人有责
- 事务持续性
- 使用通用软件
- 守法
- IT责任
- 知识产权保护
- 数据原则
- 数据资产
- 数据共享
- 数据访问
- 数据托管
- 使用常用词汇、有数据定义
- 数据安全
- 应用程序原则
- 技术无关性
- 易用性
- 技术原则
- 需求变化
- 响应变更管理
- 控制技术多样化
- 互操作性
关键原则:
- 关注点分离:将程序分解为相对无关的、功能重叠部分尽可能少的一个个模块,达到高内聚和低耦合的目的。
- 单一职责原则:每个组件或模块应负责一个特定特征或功能,或者内聚功能的集合。
- 最少知识原则(迪米特法则,LoD):一个组件或对象应该对其他组件或对象的内部细节尽可能少的了解。
- 不重复自身原则:只须在一个地方指定意图。(对于程序设计,特定功能只能在一个组件内实现,且该功能不能复制到任何其他组件内。不然修改一个地方,复制的地方也得改)
- 尽量减小前期设计:只做必要的设计。
软件架构设计面临的主要威胁及其对策
几个面临威胁的方面:
- 被忽略的重要非功能需要
- 对策:全面认识需求
- 频繁变化的需求
- 对策:关键需求决定架构
- 考虑不全面的架构设计
- 对策:多视图探寻架构
- 不及时的架构验证
- 对策:尽早验证架构
- 较高的创造性的架构比重
- 对策:合理分配经验架构与创新架构比重
- 架构的低可执行性
- 对策:验证架构的可执行性
软件架构质量
软件架构的质量也有内部质量和外部质量之分:
一般认为开发态软件架构是软件的静态架构,难以在真实环境中进行实际的运行,其质量是内部质量,包含软件架构模型、数据、描述文档和视图的质量等;
处于运行和维护演化过程的软件架构,称为运行态软件架构(动态架构),其质量是外部质量,包含基于该软件架构开发的系统的性能、可靠性、安全性等。
讨论软件架构质量问题的意义:
- 最终软件产品质量问题是当前软件开发发展过程的重要核心关注点之一
- 问题发现的越早,解决问题的代价越小
- 软件架构自身存在着很高的质量需求
软件架构质量保障的好处
- 对软件架构进行精确理解
- 为相互冲突的目标划定优先级
- 督促软件架构师更详细地编写软件架构文档
- 发现项目之间交叉重用的可能性
- 提高软件架构实践者的水平
- 有益于该组织未来所从事的项目开发
软件架构和质量属性
软件架构和软件产品质量之间的关系是相辅相成的。
一方面软件架构本身具有质量,高质量的软件架构设计会带来高质量的软件产品。
另一方面,软件最终产品的质量可以间接地反映软件架构的质量,可以进一步指导软件架构的演化和优化等。
软件产品质量与架构之间的关系
软件架构质量指标
内部质量指标是用来直接地评估软件架构自身的质量,包括软件架构文档的可读性、数据的一致性和兼容性、架构模型的完整性、软件架构的可重配置性、可维护性等。
外部质量指标是用来间接地评估软件架构的质量,这些指标其实都是基于该架构开发的最终软件系统的质量指标,这些指标不好的话,也可以间接反映软件架构存在缺陷。
内部质量指标
软件架构的内部质量是指描述软件架构文档、数据、图表和模型的质量,具体来讲,还指构成软件架构的组件、连接件、配置、数据和接口的质量。
软件架构典型的内部质量指标有(文档、数据、图表、模型)的可维护性、可重用性、可移植性、可集成性和可测试性等。
可维护性: 软件系统或组件在纠正错误,提升性能或其他属性,以及适应变化的环境的修改容易程度。
- 改正性维护是指改正在系统开发阶段已发生而系统测试阶段尚未发现的错误。
- 适应性维护是指软件适应信息技术变化和管理需求变化而进行的修改。
- 完善性维护是为扩充功能和改善性能而进行的修改,主要是指对已有的软件系统增加一些在系统分析和设计阶段中没有规定的功能与性能特征。
- 预防性维护为了改进应用软件的可靠性和可维护性,为了适应未来的软硬件环境的变化,应主动增加预防性的新的功能,以使应用系统适应各类变化而不被淘汰。
可重用性:可重用性通常是指要合理地设计系统使得系统结构或其某些组件能够在未来的应用开发中可以重复使用的能力。
可移植性:可移植性是系统能够在不同计算环境(或平台)下运行的能力。
可集成性:可集成性是使其他独立开发的系统组件能够与待开发系统协同运行的能力。
可测试性:可测试性是指通过测试(通常是基于运行的测试)使软件表露出缺陷的容易程度。
外部质量指标
软件架构的外部质量是指在软件系统运维过程中,软件系统体现出来的与软件架构有关的质量属性。
性能:是指系统的响应能力,即要经过多少时间才能对某个刺激(事件)做出响应,或者在某个时间段内所能处理的事件的个数。
性能主要关注如下问题:
- 客户端响应时间增加、吞吐量降低以及服务器资源过载使用。
- 内存消耗增加、以及过多的缓存未命中(在缓存中不能找到需要数据)。
- 数据库服务器处理增加。
- 网络带宽消耗增加
可用性:是指系统正常运行的时间比例。
系统的可用性通常会收到系统错误、基础结构问题、恶意攻击、系统负载等因素的影响。
可用性主要关注如下的问题:
- 物理层(如数据库服务器或应用服务器)发生故障或变得无响应,会导致整个系统运行失败
- 如果系统不能及时处理由于网络配置或网络阻塞引起的大规模的负载,采用拒绝服务、阻止授权用户访问系统等中断系统操作。
- 不恰当的资源使用会降低可用性。例如,过早的获取资源并长期持有会导致资源匮乏,并降低了处理其他并发用户请求的能力。
- 应用程序中的错误或故障会导致系统范围内的失败。
- 频繁的升级会降低系统的可用性。
- 网络故障或导致应用程序的不可用。需要考虑如果处理不稳定的网络连接。
- 在应用程序中考虑信任边界并保证子系统采用了一定程度的访问控制或防火墙技术,同时也要考虑外延数据的有效性,来提高系统的弹性和可用性。
可靠性:指的是系统能够保持正常运行的能力。
可靠性主要关注如下的问题:
- 系统崩溃或变得没有回应时,探测失效根源并自动启动失效备援,或者将负载转送到备份系统。
- 输出不一致时,通过执行插桩探测性能缺陷根源,并通过系统输出相关信息。
- 由于外部因素(例如网络不可用)导致系统失效时,寻找合适的方式处理不可靠的外部系统、失效的交互以及失效的事务等。
安全性:衡量系统在向合法用户正常提供服务的情况下,阻止企图非授权使用,或者抗拒拒绝服务攻击,并阻止信息泄露和丢失的能力。
安全性主要关注的问题有:
- 欺骗用户身份
- 由恶意输入引起的危害
- 数据篡改
- 拒绝用户行为
- 信息泄露及敏感数据丢失
- 由于DoS攻击导致的服务中断
易用性:包括可学习性、效率、可记忆性、错误避免、错误处理、满意度
易用性主要考虑如下的问题:
- 完成一项任务需要太多的交互(大量的点击),保证对屏幕输入流程及交互模式的设计最大限度地提高易用性。
- 在多步骤的接口中存在不正确的流程步骤,考虑结合工作流来简化多步骤操作。
- 数据元素和控制没有很好进行分类,选择合适的控制类型并采用公认的UI设计模式对控制符及内容进行设计。
- 给用户的反馈非常薄弱,特别是在应用程序发生错误,异常或没有响应的情况下,考虑采用相关技术最大限度地提高用户交互性。
软件架构质量保障和评估方法
软件架构评估的目的是判断该架构是否实现了风险承担者的质量需求。
评估准备
软件架构评估作为软件开发过程的一个重要步骤,首先需要建立规范的评估文档(类似于软件测试中测试用例),这类文档主要供评估人员和风险承担者参阅和交流。
风险承担者
风险承担者就是在该架构及根据该架构开发的系统中有既得利益的人。
系统生产者:架构师、开发人员、维护人员、测试人员、集成人员、标准专家等等;
系统消费者:客户、最终用户、应用开发者、任务专家
系统服务人员:系统管理员、网络管理员,服务代表
接触系统与系统交互的人:团体代表、系统架构师、设备专家
参与者
评估参与者会构成一个评估团队,人员包括评估小组负责人、评估负责人、场景记录员、进展记录员、过程观察员、过程监督员和提问者等。
评估时机
架构评估的时机一般在架构明确之后,具体实现开始之前。
早期评估主要发生在初期阶段,即完成高层次的架构以及部分高优先级的架构决策。
中期评估主要发生在架构设计实施部分精化之后。
后期评估主要发生在系统已经被完整地设计、实现并部署之后。
评估技术
基于问卷调查或检查表的软件架构评估技术:
优点:
这一评估方式比较自由灵活,可评估多种质量属性,也可以在软件体系结构设计的多个阶段进行。
缺点:
由于评估的结果很大程度上来自评估人员的主观推断,因此不同的评估人员可能会产生不同甚至截然相反的结果,而且评估人员对领域的熟悉程度、是否具有丰富的相关经验也成为评估结果是否正确的重要因素。
基于场景的软件架构评估技术:
在软件架构评估中一般采用激励、环境和响应三方面对场景进行描述。
基于度量的软件架构评估技术:
软件架构评估的收益和成本
常见收益:
- 把各个风险承担者召集到一起。
- 迫使对具体的质量目标做出清楚的表述。
- 为相互冲突的目标划分优先级。
- 迫使对软件架构做出更清楚的解释。
- 提高架构文档的质量。
- 发现项目之间交叉重用的可能性。
- 提高架构实践水平。