什么是软件体系结构风格?

软件体系结构风格(Architectural Styles)是描述特定系统组织方式的惯用范例,强调了软件系统中通用的组织结构。

一个软件体系结构风格定义了构件和连接件类型的符号集,及规定了它们怎样组合起来的约束集合。

常见体系结构风格(概述)
主程序 - 子程序 风格

主程序-子程序风格是结构化程序设计的一种典型风格,从功能的观点设计系统,通过逐步分解和细化,形成整个系统的体系结构。
在这里插入图片描述

管道-过滤器风格

构件:过滤器。它对输入流进行处理、转换。处理后的结果在输出端流出。
【过滤器之间是相互独立的,无需进行状态信息的交互(主要特点)。每个过滤器都有输入输出,过程是:输入流->进行处理->输出流】

连接件:管道。位于过滤器之间,起到信息流的导管作用

  • 当处理数据流时,每一处理步骤都由一个过滤器(构件)完成,这些过滤器由携带数据的管道(连接件)连接起来
    过滤器:从输入源读入一个数据流,并从输出池写出另一个数据流
    管道:把一个过滤器的输出传送到另一个过滤器作为输入
  • 用不同的组合方式连接过滤器可以提供处理数据流的不同方式

可以解决的问题:

  • 处理数据流,自然的分为几个步骤(可理解为需要做一系列独立的增量计算/转换)
  • 可能需要重组步骤
  • 不连续的步骤不共享信息/状态

解决方案:

  • 过滤器逐渐消耗或传递数据
  • 过滤器可以有参数
  • 数据源输入(数据文件等)
  • 输出至数据接收器
  • 从数据源到数据接收器的过滤器序列给出一个处理管道

优点:

a. 使得软构件具有良好的隐蔽性和高内聚、低耦合的特点,系统中已有的过滤器很容易用于新的待设计系统。
【隐蔽性:构件本身是封装好的实体,高内聚、低耦合:因为构件彼此之间是独立的】
b. 允许设计者将整个系统的输入/输出行为看成是多个过滤器的行为的简单合成【便于划分功能】
c. 支持软件重用。
【只需要控制过滤器的输入流,就可以将两个过滤器连接起来】
d. 具有较强的可维护性和可扩展性。
【可维护性:更新和升级–增加、修改过滤器,只需要考虑涉及到的过滤器,其他不相关的不受影响】
【可扩展性:添加新的功能,只需要在原有基础上添加新的端口】
e. 支持并行执行。
【构件之间相互独立,所以可以并行执行】
f. 为系统的性能分析提供了方便。【吞吐量、死锁、计算机正确性等属性】

缺点:
a. 通常导致进程成为批处理的结构。
【过滤器是对输入的批量转换处理,对输入和输出有相应的说明限制】
b. 全局变量的共享很困难,通信、交互性不强
【过滤器对输入流有着严格的限制】
c. 当数据传输量很大而又有很多小的过滤器时代价较大
【因为在数据传输上没有通用的标准,每个过滤器都增加了解析和合成数据的工作,这样就导致了系统性能下降,并增加了编写过滤器的复杂性。】
d. 并行效果并不理想
【数据传输占用系统的执行时间】
f . 出现错误时如何做处理比较困难

在这里插入图片描述
举例1:(媒体播放器)
在这里插入图片描述
举例2:(编译器)
在这里插入图片描述

面向对象的体系结构风格
在这里插入图片描述
构件:对象,或者说是抽象数据类型的实例
连接件:通过函数和过程的调用

●主要特点
一对现实世界的自然建模方法
一封装(数据表示和相关的基本操作被封装,限制访问)
一继承(共享功能定义,可以在继承一个已定义好的类的所有特征的基础上逐渐定义更具体的对象)-重用
●对象(构件实例)
一负责维护自身表示的完整性
一这种表示对其他对象是隐藏的
一通过消息调用通信

优点:

  1. 强调设计,全面的系统考虑,和现实世界的对应关系
  2. 对象对客户隐藏了实现的细节,可以在不影响客户的情况下改变对象的实现(低耦合,高重用,可维护),方便系统升级
  3. 内部表达的保护(封装数据/状态的完整)

缺点:

  1. 对象交互时需要知道彼此的标识
  • 与此相反,管道过滤器系统中的过滤器之间的交互不需要知道系统中其他过滤器的存在
  • 一个对象标识改变时,必须修改每一个显示调用了它的对象(导入/import了该对象的名字的模块)
  1. 多个对象对同一资源的访问(A和B同时需要使用C)
  • 状态的改变和维护
  • 需要做更改时(B对C需求的影响,可能会对A有牵连)
  1. 采用面向对象,分解出的是小粒度的基本类或对象。从系统总体结构到构件的设计,缺乏有效的描述和设计方法。
    【就是一开始做体系结构设计,分解出的构件很难做到对象这么精准】

分层的体系结构风格

典型范例(内、外分层)
在这里插入图片描述

典型范例另一表现方法(上、下分层)
在这里插入图片描述
构件:组织为层次结构,每一层给外层提供服务,又作为它内层的客户;某些系统中,内层只对相邻的层可见
连接件:层间的协议,定义了层次间的交互方式
在这里插入图片描述

可以解决的问题:
●系统有不同级别的抽象层
●请求向下传递,通知向上传递
●层间需要稳定的界面
●,不同层次将来可能会变化
●团队编程可能需要分解,细化

解决方案:
●从最底层开始
●逐渐向上建立高层
● 同一级别的抽象放在同一层
●无组件跨越两层
●各层规范接口界面
●尽量维持下层简单
●尽量在下层做错误处理

优点
a.支持基于抽象程度逐渐递增的系统设计
【就是可以由简单到复杂】
b.层重用
【层与层之间通过服务接口进行交互,所以只要接口不变,层就可以重用】
c.支持功能增强
【因为每一层基本只影响相邻两层,所以对功能的修改最多影响相邻两层】
e.扩展性,可维护性
【可以添加新层,改变当前层】

缺点
a.行为改变的传递性
b.效率低下
【上面的层过分依赖下面的层提供服务,相关数据传达时间长,数据传输低效】
c.层次大小的难以清晰界定
【并不是每个系统都可以很容易地划分为分层的模式,有时候为了性能考虑,要把一些综合起来,避免功能模块跨越多层较难】
d. 缺少公认合适的、正确的层次抽象方法。
【层次太少,分层不能完全发挥这种风格的可重用性、可更改性和可移植性上的潜力。层次太多,会引入不必要的复杂性和层间隔离冗余以及层间传输的开销。】

举例:分层通信协议。在这一应用领域中,每一层提供一个抽象的功能,作为上层通信的基础。较低的层次定义低层的交互,最低层通常只定义硬件物理连接。

仓库风格
构件:

  • 中央数据单元,代表系统当前的各种状态
  • 相对独立的构件集合,对中央数据单元进行操作

两种控制策略的选取产生两个主要的子类

  • 输入数据流中的事务指令触发系统相应进程的执行:则仓库是传统
    数据库体系结构
  • 中央数据结构的当前状态触发系统相应进程的执行:则仓库是一个黑板体
    系结构;黑板体系结构是仓库体系结构的特殊化

黑板系统
在这里插入图片描述
3大组成元素:
(1)知识源:包含独立的、与应用程序相关的知识。

  • 知识源之间不直接通信,它们之间的交互通过黑板完成
  • 它们可以读当时的不完全解决方案
  • 它们可以建议对该方案进行修改
    (2)黑板数据结构:黑板数据是按照与应用程序相关的层次来组织的解决问题的数据。知识源通过不断的改变黑板数据来解决问题。一个中央黑板数据结构用来描述对一个问题的不完全解决方案(问题求解状态)。
    (3)控制组件:监视黑板状态,安排知识源活动进程

需要解决的问题

  • 需要对复杂数据解释(比如信号处理,语音识别)
  • 对某问题没有决定性的解决方案
  • 对该问题的不同部分可能需要不同代表性的解决框架
  • 对于不同的问题解决者,可能没有合理组合它们的固定策略
  • 可能需要和不确定的知识打交道

*解决方案 *

  • 问题解决者独立解决部分问题
  • 共享一个数据结构(黑板)
  • 一个中心控制器管理对黑板的访问
  • 黑板可以被结构化(比如抽象为不同层),这样问题解决者可以在不同层次上工作

典型范例:信号处理领域中的语音和模式识别。另一应用是松耦合代理数据共享存取。
在这里插入图片描述
语音识别在这里插入图片描述

优点

  • 在有限的范围内容易实验不同的问题解决者和诱导启发式的控制方法
  • 知识源可以重用;规模伸缩性好(方便添加新数据)
  • 系统在有限的范围内可以容忍哪些不可靠的问题解决者,这可以通过保护/修改黑板实现

缺点

  • 测试困难
  • 难以保障最佳解决方案
  • 控制策略通常需要启发诱导
  • 计算开销大(工作量可能比较大,浪费多)
  • 并行处理可行,可实际上需要同步/加锁机制保证数据结构的完整性和一致性,这增加了系统的复杂度
  • 不同数据源对于共享数据结构要达成一致,这使得对黑板数据结构的修改较为困难。

独立组件风格

通信进程

  • 消息在命名的参与者间传播
    AND 事件驱动
  • 未命名的参与者间隐式调用

通信进程风格
构件:独立进程
连接件:消息传递(点对点、同步或异步、RPC)

在这里插入图片描述

事件驱动

基本思想

  • 不直接调用一个过程,而是发布或广播一个或多个事件
  • 其他构件通过注册与事件关联的进程来表示对该事件的兴趣
  • 事件发生时,系统会调用所有注册了该事件的过程
  • 这样一个事件的激发导致其他模块中过程的隐式调用
    主要特点
  • 事件发布者不知道哪些构件会受到事件的影响
  • 构件不能对事件的处理顺序或者事件发生后的处理结果做任何假设

构件:对象或进程

  • 接口定义了允许的接受事件和发布事件
    连接器:事件-过程的绑定
  • 过程注册感兴趣的事件
  • 构件通过宣布事件来交互
  • 当接收到事件时,关联的过程被隐式调用
  • 调用顺序不确定

显示调用和隐式调用
在这里插入图片描述

优点:

  1. 问题分解
  • 计算和协作分开来:对象更加独立
  1. 系统维护和重用
  • 没有静态,固定的对对象名字的依赖
  • 系统重用比较容易:需要用新构件时只需要注册一个系统事件就可以引入到系统中
  • 系统演化比较容易:不改变其他构件接口的情况下,构件间的互相取代很容易;替换某构件不影响其他构件,升级简单
  • 集成容易
  1. 效率
  • 可并行调用

缺点:
1.问题分解

  • 构件放弃了自身对系统计算的控制:当发布事件时,无法保证其他构件会做出反应,也无法保证事件被处理的先后顺序
  • 数据交换问题:有时数据可被一个事件传递,但另一些情况下,基于事件的系统必须依靠一个共享的仓库进行交互。在这些情况下,整体性能和资源管理可能会成为问题
  • 很难对系统正确性进行推理:和传统调用功能行为检验不同,不能只考虑过程前后的条件,而是发布事件时事件激发的上下文
  1. 系统维护和重用
  • 需要一个关于谁对什么感兴趣的中心黄页服务:事件,注册,策略调度
  1. 效率
  • 间接通信意味着一些性能损失

常见案例
数据库管理系统中数据-致性约束的实现
●程序调试工具中变量监视器对变量值的刷新
【在某系统中,编辑器和变量监视器可以登记相应Debugger的断点事件。当Debugger在断点处停下来时,它声明该事件,由系统自动调用处理程序。
Debugger本身只声明事件,并不关心哪些过程会启动,也不关心这些过程做什么处理。】
●集成开发环境中编译器支持的自动语法检查
●天气预报手机短信

完成这篇博文参考了许多资料,感谢肖亮老师提供的PPT
还有简书上的Mikito_k参考
以及知乎上的王boy参考

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐