【软件体系结构】笔记

OverView

软件价值观

  • 行为价值:让程序完成指定的功能
    • 大多数程序员认为这是主要工作——完成功能、发现并修正bug
  • 架构价值:让程序保持易于修改
    • 容易被忽视,但这种价值更符合“软件”的存在逻辑
    • 软件之所以存在并发展,就是因为它比硬件“软”
    • 用户的需求在变、股东的方向在变

历史

  • 在20世纪90年代,有一个协调一致的努力来定义和编纂该学科的基本方面,研究工作集中在体系结构风格(模式)、体系结构描述语言、体系结构文档和形式化方法上。
  • 卡内基梅隆大学的Mary Shaw和David Garlan在1996年写了一本名为《软件架构:新兴学科的视角》的书,这本书促进了诸如组件、连接器和风格等软件架构概念。

定义

Wikipedia

  • Software architecture is the set of structures required for analyzing a software system and the field of creating such structures and systems.
  • Each structure consists of software elements, the relationships between them, as well as the properties of both the elements and the relationships. IEEE 1471-2000 Software architecture is the fundamental organization of a system, embodied in its components, their relationships to each other and the environment, and the principles governing its design and evolution.

Programming Paradigms

  • 结构化程序设计 (Structured Programming)
  • 面向对象程序设计 (Object-Oriented Programming)
  • 函数式程序设计 (Functional Programming)

结构化程序设计

Dijkstra是发现程序设计这个学科、并将其发展成一门科学的人之一 对于’不管多简单的程序,只要程序员稍稍忽视一点细节,程序就会看似正确,却以令人诧异的方式出错‘的问题,Dijkstra 的方案:程序的形式化证明

  • 用欧式几何的思路:公理、引理、定理……
  • 程序员可以用可证明正确的结构,搭建出更大的程序
  • 顺序、分支、循环、调用、递归…… 结构化程序设计由此诞生了
  • 结构化程序设计取得了巨大的成功
    • 因为它使得我们能够将任务层层分解(直到无穷)
  • 程序的形式化证明至今未能广泛应用(太累了!)
    • 取而代之的是更“科学”的方法

面向对象程序设计

面向对象

  • 数据函数的组合
  • 封装、继承、多态 对于软件架构师来说,面向对象是一种通过使用多态获得的,对系统中每个源代码依赖的绝对控制能力
  • 允许软件架构师创建插件架构,让高级策略模块独立于低级细节模块
  • 低级细节被下放到插件模块中,可以独立于高级策略模块进行部署和开发

函数式程序设计

  • 函数式程序设计是以函数为核心的程序设计范式
  • 所有的并发问题都是由于变量(内存里的值)可变引起的
    • 函数式程序设计中,变量“不变”
    • 将尽量多的功能实现在不可变组件中,尽量少的实现在可变组件中

总结

  • 结构化程序设计
    • 剥夺了程序员使用跳转语句的自由,赋予了软件模块化的能力
  • 面向对象程序设计
    • 剥夺了程序员使用函数指针的自由,赋予了软件插件化的能力
  • 函数式程序设计
    • 剥夺了程序员使用赋值语句的自由,赋予了软件并行化的能力

Principals

  • 设计原则
    • 如何将函数和数据组织为类
    • 将砖拼装为房间
  • 组件原则
    • 如何将类组织为组件和软件
    • 将房间拼装为大楼

设计原则

  • Single Responsibility Principle
    • 一个类应该只有一个引起它变化的原因。这意味着一个类应该只负责一个功能或职责,并且所有行为都与该职责紧密相关。
  • Open-Closed Principle
    • 软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
  • Liskov Substitution Principle
    • 子类对象应该能够替换其基类对象,并且不破坏程序的正确性。这个原则强调了继承关系中子类对基类行为的一致性。
  • Interface Segregation Principle
    • 可能被不同模块调用的函数,应抽象为不同的接口
  • Dependency Inversion Principle
    • 高层模块不应该依赖于低层模块,两者都应该依赖于抽象;抽象不应该依赖于细节,细节应该依赖于抽象。

组件原则

  • 组件聚合原则:哪些类组织到哪个组件?
    • Reuse/Release Equivalence Principle
      • 重用的粒度,就是发布的粒度
    • Common Closure Principle
      • 把那些会因为相同原因而同时修改的类组成一个组件,把那些会因为不同原因而分别修改的类组分隔为不同组件。
      • 组件层次的单一责任原则SRP。
      • 可维护 > 可重用。
      • 一次修改最好集中在一个组件,不希望横跨多个组件。
      • 修改一个组件,不相关的组件无需重新编译部署。
    • Common Reuse Principle
      • 不要强迫组件的用户依赖它不需要的东西(类、接口等)
      • 组件层次的接口隔离原则ISP
  • 组件耦合原则:组件之间如何交互?
    • Acyclic Dependencies Principle
      • 组件依赖图中不能有环
      • 如何消除环?
        • 使用依赖反转原则DIP
        • 加入新组件管理权限
        • 系统不是被自顶向下设计出来的,而是随着系统增长被演化出来的
  • Stable Dependencies Principle
    • 依赖方向应从不稳定到稳定
    • 不应让不稳定的组件被难于修改的组件依赖
    • 不稳定性 I=Degree_out/(Degree_out + Degree_in)
    • 依赖方向为𝐼值降序方向
  • Stable Abstractions Principle
    • 组件的稳定性应与它的抽象程度对应
    • 越抽象越稳定,越稳定越抽象
    • 依赖方向应该为具体依赖抽象
    • 抽象度 A=N_abstract_class/N_class
    • 组件应尽量接近“主序线”: 𝐷𝑖𝑠𝑡𝑎𝑛𝑐𝑒= |𝐴+𝐼-1| 组件历史
  • 最初,程序所在的位置是固定的,后来,应用越来越大,编译时间越来越长,函数库诞生了。
  • 由于位置是固定的,应用被限制在了函数库之前的位置,一旦超出预留的大小,应用程序就需要被切分。
  • 人们终于意识到,程序需要能够重定位。如何实现程序的重定位?
    • 加载顺序
    • 动态链接
  • 可单独编译、可独立发布、可动态加载、可动态链接的软件模块 ——组件

组件风格

组件(Component)

  • 一组函数和数据的封装
  • 组件间通过接口沟通
  • 组件可被同样接口的组件替换
  • 组件被充分测试,足够鲁棒
  • 组件有足够的文档说明 组件风格
  • 组件风格关注可复用的组件。
  • 实际制造满足需要的组件非常困难。
  • 组件模型为制造组件提供模板规范。 组件模型
  • Enterprise JavaBeans (EJB) 模型
  • Component Object Model (COM) 模型
  • .NET模型
  • X-MAN组件模型
  • Common Object Request Broker Architecture (CORBA) 组件模型

管道过滤器风格

  • 过滤器
    • 每个组件都有其输入和输出
    • 组件的处理过程就是根据输入产生输出
  • 管道
    • 连接两个组件,将一个组件的输出变成另一个组件的输入
  • 过滤器通过管道连接为有向无环图,形成复杂功能
  • 对比:
    • 组件风格关注组件提供/需要的接口
    • 管道-过滤器风格强调数据流的衔接

优势

  • 弱耦合性:各个过滤器相互独立,设计者可以将整个系统的输入、输出特性理解为各个过滤器功能的简单合成。
  • 易于重用:任意两个过滤器只要相互间传输的数据格式一致,就可以连接在一起。
  • 易于维护扩展:新过滤器可以很容易加入到系统中,旧的过滤器可以很容易被新的过滤器替代。
  • 易于分析测试:每个过滤器可以独立测试。
  • 天然并发性:每个过滤器都可以独立运行,形成流水线pipeline。

缺点

  • 交互性差:由于过滤器的传输特性,管道过滤器模式通常不适合于交互性很强的应用。尤其是在系统需要逐步显示数据流变化的过程时,因为增量显示和过滤器的输出数据差距太大。
  • 维持通信困难:维持两个相对独立但存在某种关系的数据流之间的通信可能很困难。
  • 通信效率低:设计者也需要在数据传输时被迫使用底层公共命名,导致过滤器必须对输入、输出管道中的数据进行解析或反解析的额外工作。

总结

  • 管道-过滤器风格关注可连接的组件
  • 将组件功能抽象为从输入数据到输出数据的过滤过程
  • 通过管道,连接过滤器,形成复杂的数据处理功能
  • 天然对分布式并发特性具有良好适应
  • 管道中的传输过程有待改进

分层风格

优势

  • 分层符合人类“分而治之”的思维方式。
    • 结构化程序设计之所以能够取得巨大的成功,正是因为它使得我们能够将任务层层分解。
  • 耦合性低。
  • 重用性高。
  • 可维护性高。
  • 无环依赖原则(Acyclic Dependencies Principle,ADP)
    • 组件依赖图中不能有环。
    • 有向无环图一定能分层。

总结

  • 分层风格关注组件间的偏序关系
  • 将系统功能层层分解,降低耦合性,提升重用性
  • 最常见的分层风格是三层架构
  • 层与层之间隔离有利于快速部署
  • 层与层之间协同有利于性能效率

事件驱动风格

  • 以事件衔接程序之间的数据流
  • 程序产生事件,触发其他程序的功能
  • 事件驱动风格可以认为是广义的管道-过滤器风格
  • 优缺点与管道-过滤器风格类似
  • 事件驱动风格的程序框架通常是提供事件等待/触发机制的中间件

共享数据风格

黑板风格

  • 黑板模块:全局存储空间
  • 知识源模块:处理知识、更新黑板
  • 控制模块:调度可用的知识源模块

总结

  • 黑板风格:与其移动数据,不如共享数据!
  • 数据中心风格:与其移动数据,不如移动程序!

客户端服务器风格

  • 服务器:拥有资源,提供服务
  • 客户端:建立会话,提出请求

总结

  • 天然的两层分层风格
    • 如何将三层风格变为两层风格?根据通信量切分
  • 问题:随着资源规模和请求吞吐量需求增大,服务器成为瓶颈
  • 解决方案
    • 端到端风格
    • 端-边-云架构

端到端风格

Peer-to-Peer, P2P

  • 去中心:所有参与者在网络中是平等的
  • 每个参与者都提供一部分资源(计算、存储、带宽等)
  • 有效解决了客户端-服务器风格中,服务器的资源瓶颈 核心优势
  • 全局内容发现
  • 高效的内容索引

端到端架构将参与者重新组织为逻辑网络

  • 非结构化网络
    • Gnutella、Gossip、Kazaa……
    • 易于组织,支持参与者随时加入离开
    • 难以发现内容
  • 结构化网络
    • 分布式哈希表DHT
    • 每个参与者、内容均与键值联系
    • 易于全局内容发现,但存在均衡问题

面向服务风格

  • 将软件分解为多个独立的服务
  • 服务:独立的功能单元,可通过通信协议与之进行远程沟通
    • 代表可重复的业务活动
    • 具有相对完整的功能
    • 对用户是黑盒,用户无需关心服务的内部实现
    • 可以调用其他服务完成功能

对比

  • 与组件风格对比
    • 在组件风格中,通常支持多个组件在同一个进程中
    • 服务更强调独立性,通信协议均为网络协议(支持跨进程通信)
  • 与管道-过滤器风格对比
    • 过滤器之间通过管道传递消息,不关心消息被用来做什么了
    • 服务接受的是请求,并须要针对请求给出答复
    • 与事件驱动风格也有类似的区别
  • 与分层风格对比
    • 只要服务与服务之间的“调用”关系不存在“递归”,就也属于分层风格
    • 万一服务之间有递归呢?
  • 与客户端-服务器风格对比
    • 客户端-服务器风格中,通常服务器是整个系统中的单个中心
    • 面向服务风格中,服务可以有很多

近亲

  • 糅合(Mashup)
    • 将两种以上使用公共或者私有数据库的Web应用加在一起,形成一个整合应用
  • 软件即服务(SaaS,Software as a Service)
    • 让用户能够通过互联网连接来使用基于云的应用程序
  • 云计算(Cloud Computing)
    • 将来自用户的任务请求分解成数个小程序,利用网络“云”上多部服务器的资源进行并行处理,得到结果并返回给用户

组成

  • 服务提供者
    • 向服务中间人注册自己提供的服务,答复服务请求者的请求
  • 服务请求者
    • 要求服务中间人查找所需服务,向服务提供者提出请求
  • 服务中间人(服务注册、服务检索)
    • 注册服务提供者,给服务请求者反馈服务提供者信息
    • 可以分为公有中间人、私有中间人

无共享风格

Shared-Nothing Architecture

  • 分布式系统风格
  • 每个更新操作只由集群中的一个结点来完成
  • 与Shared Everything相对,旨在消除数据竞争 数据库两大类应用
  • 联机事务处理(OLTP,On-Line Transaction Processing)
  • 联机分析处理(OLAP,On-Line Analytical Processing) 新兴数据库
  • 特点
    • 容量需求大
    • 数据增加多
    • 数据更新少
    • 查询效率尽量快
  • 数据仓库(Data Warehouse)
  • 数据湖(Data Lake)
Licensed under CC BY-NC-SA 4.0
Last updated on Jan 13, 2025 00:00 UTC
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy