目录
声明:仅做学习参考,本文是中国大学MOOC北京交通大学软件体系结构翼振燕、曾立刚课程的笔记
1.1软件体系结构基础知识
软件开发历史
- 1970之前汇编语言
- 适用于小型程序
- 1970-1980高级语言
- 面向过程理论
- 数据流/控制流设计方法
- 1980-1995应用程序开发框架
- 应用程序开发库(类库/函数库)
- 面向对象理论
- 面向对象建模以及设计技术
- 1995之后
- 1995JAVA语言诞生年
- 应用程序开发框架:J2EE,.NET
- 组件技术:COM/DCOM,CORBA
- 对象建模/设计标准化:UML
- 未来
- 模型驱动开发:MDA
软件体系结构定义
- 软件架构
- 软件元素:功能、接口、程序、类模块、层、子系统、客户端/服务器
- 可见属性:提供服务、性能特征、错误处理、共享资源使用
- 关系:这些元素之间的组合机制
- 架构时商业和技术决策的结果
- 软件体系结构组成
- 构成元素——组件
- 交互机制——连接器
- 简而言之,构成系统的组件和组件间的关系或交互机制
- 软件体系设计=分解+组合

- 分解/组合
- 降低软件设计和构建的复杂度
- 控制软件开发的风险
- 提高组织管理效率
- 组件是抽象的、概念的词语、在不同的场景中可以是不同的特定对象(了如:模块、子程序、层、包、类)
- 软件体系结构定义(非常多)


- 软件体系结构定义可以参考CMU网站
- http://www.sei.cmu.edu/architecture/definitions.html
- 连接器
- 组件之间的交互规则或机制
- 架构
- 软件架构的功能属性
- 软件架构中满足功能性需求的属性
- 软件架构的非功能属性
- 软件架构中满足非功能属性的属性
- 性能
- 可移植性
- 灵活性/可扩展性
- 可靠性/安全性
- 软件架构中满足非功能属性的属性
- 架构特点
- 架构是系统可看成一个整体的高层次的抽象
- 在架构层次,隐藏所有的实现细节
- 架构必须支持系统的功能性需求
- 架构必须满足系统的质量属性(非功能性需求):性能、安全性、可靠性、灵活性、可扩展性
- 软件架构的功能属性
- 框架
- 框架是解决特定问题的可重用的应用程序结构
- 提供解决特定问题的必要的、基本的组件
- 组件间的交互机制和约束
- 提供基于框架开发的应用程序的上下文或环境
- 通常而言,框架以类库形式提供
- 通常而言,框架以类库形式提供,例如Struts架构,.NET架构,JavaEE框架
1.2软件体系结构的影响结果
- MCV架构(ModuleViewControlor)
- 耦合度非常低,模块之间高内聚,称为当前技术环境的主流架构
- 架构设计活动
- 创建系统的业务用例
- 理解用户需求
- 创建或选择合适架构
- 为架构建立文档
- 分析评价架构
- 实现基于架构的系统
- 确保系统实现符合架构
- 架构设计为什么重要
- 架构是利益相关者交流的工具
- 架构可以体现早期的设计决策
- 架构定义了实现的约束
- 架构描述了组织架构
- 架构可以保证或禁止系统的质量属性
- 通过分析架构可以预测系统的质量
- 架构设计可以有效规避风险
- 架构可以帮助原型演化
- 架构可以使成本和周期估算更准确
- 架构是一种可重用模型
- 软件生产线共享架构
- 可以使用外部开发的架构来实现系统
- 架构的价值
- 架构对于组织和技术方面都存在价值
- 组织层次
- 有助于组织内部,或者客户和开发商间的沟通
- 提供系统更高层次的抽象
- 成本和风险评价
- 工作分配和项目规划
- 技术层次
- 满足系统需求和目标
- 规定详细设计、构建、测试的约束
- 确保对系统的灵活划分
- 减少维护和演化的成本
- 增加重用,与旧的、与第三方软件结合
- 好架构的特征
- 弹性
- 简单
- 可实现
- 功能职责的清晰分解
- 模块高内聚,模块之间低耦合(也是保证弹性的)
- 平衡职责分布
- 平衡经济和技术约束
2. 软件架构建模--4+1视图(Kruchten)
- 模型
- 对无法直接观测的对象的可视化描述或近似
- 模型是对现实的简化
- 模型提供了系统的蓝图
- 模型可以是结构化的,着重描述系统的组织架构
- 模型也可以是行为化的,着重描述系统的动态行为
- 对无法直接观测的对象的可视化描述或近似
- 为什么建模?
- 建模可以让我们更好地理解我们建构的系统
- 通过建模,实现了四个目标
- 对系统进行可视化
- 规约系统的结构或行为
- 用于指导构建系统的模板
- 将设计决策形成文档
- 架构视图
- 架构是从特定的视角对系统的简化描述(抽象),覆盖特定的关注点,忽略那些与该视角无关的实体对象
- 架构视图过度强调开发方面,没有解决所有利益相关者关注的问题
- 软件系统的利益相关者:用户、开发者、系统工程师、项目经理
- 软件工程师极力添加更多的信息到架构图中,导致架构图复杂
- 解决方案
- 采用多个并发视图,每个视图用不同的符号展示不同的关注点
- “4+1”视图模型为大的、复杂的架构建模
- 逻辑视图
- 系统的功能抽象。将系统分解为多个功能组件,描述其功能关系
- 开发视图
- 系统的详细设计与实现的抽象
- 过程视图
- 主要描述系统的非功能性需要和系统的运行特征(性能)
- 物理视图
- 描述系统的硬件拓扑结构,以及软件组件在硬件上的部署
- 场景视图
- 功能性需求的场景如何
- 一般用用例图描述,时序图,流程图
- 逻辑视图
3.1软件架构建模--建模工具和语言(一)
- 建模工具和语言
- 面向对象建模语言在70年代中期到80年代后期之间出现;
- 1989年-1994年间,面向对象建模方法从不到10中发展到50多种
- 面向对象建模语言
- The Booch方法在项目设计和建构阶段极具表达力
- OOSE对业务需求分析提供了很好的支持
- OTM-2对于数据密集型信息系统的分析极具表达力
- UML语言
- 适用于对软件密集型系统的物件进行
- 可视化
- 规范化
- 建构
- 文档化
- UML组成
- 建模元素
- 关系
- 扩展机制
- 图
- UML建模元素
- 结构化元素
- 类、接口、协作、用例、组件、节点
- 行为元素
- 交互、状态机
- 分组元素
- 包、子系统
- 其他元素
- 注释
- 结构化元素
- UML关系
- 依赖
- 一个对象依赖于另一个对象,一个对象发生变化的时候依赖于它的对象也会发生变化
- 关联
- 表述了两个元素之间有结构化的关系
- 单向关联关系
- 一个元素对另一个元素是单向可见的
- 分类
- 聚合
- 聚合关系表示一个对象是另外一个对象的一部分,但它们的生命周期可以不一样
- 组合
- 组合关系表示一个对象是另外一个对象的一部分,他们的生命周期必须是一样的
- 聚合
- 泛化(继承关系)
- 泛化关系就是派生类对于父类的继承
- 实现
- 类对接口的实现
- 依赖
- 扩展机制
- 构造型
- 标记值
- 约束
- 适用于对软件密集型系统的物件进行
- 模型是从某个也顶视角对于一个系统的完整描述
- UML常用的图
- 用例图、类图、对象图、组件图、部署图、活动图、状态图、通信图、时序图
- 图
- 图是模型的一个视角
- 从某个特定的利益相关者的视角展示系统
- 提供系统的局部描述
- 与其他图语义一致
- 在UML中,有13种标准图
- 静态视图
- 用例图、类图、对象图、组件图、部署图、包图、组合结构图
- 动态视图
- 时序图、通信图、状态图、活动图、定时图、交互概览图
- 静态视图
- 用例图
- 描述系统功能性需求

- 长方形代表系统的边界,椭圆形是以用例代表了系统的功能,火柴人代表系统参与者,线代表用例之间或者参与者和用例之间的交互
- 系统参与者和系统进行交互的角色或者外部设备,另外一个系统或软件系统
- 业务用例图
- 从用户视角描述系统功能性需求
- 开发早期阶段使用
- 目的
- 确定系统上下文
- 捕捉系统需求
- 验证系统架构
- 驱动系统实现和测试用例生成
- 由领域专家和系统分析师完成
- 类图
- 在模块设计里面常用的一个图,基本上每个面向对象系统再做详细设计的时候都需要用到类图
- 类图里面说明了系统里有哪些类,类里面的属性,类里面的方法还有类之间的关系
- 捕捉系统的词汇表
- 由分析师、设计师和开发人员完成
- 图是模型的一个视角
3.2软件架构建模--建模工具和语言(二)
- 对象图

- 描述对象实例和对象实例之间的关系
- 某一个时间点的系统的快照
- 在分析和设计阶段绘制
- 目的
- 描述数据/对象结构
- 某个时间点的系统快照
- 由分析师、设计师、开发人员共同绘制
- 组件图

- 描述系统实现的物理结构
- 作为架构设计的一部分
- 表明系统由哪些组件或模块组成,模块/组件通过什么接口交互的
- 目的
- 组织源代码
- 构建可执行的release版本
- 表述物理数据库
- 由架构师和程序员绘制
- 部署图

- 描述系统硬件的拓扑结构
- 每个立方体都代表一个节点,长方体代表网络,自定义符号表示
- 作为架构设计的一部分
- 目的
- 描述组件的硬件部署
- 识别性能瓶颈
- 由架构师、网络工程师、系统工程师共同绘制
以上都是描述系统静态方面,下面是动态方面
- 时序图
- 描述面向时间的动态行为

- 目的
- 对控制流进行建模
- 描述用例场景
- 通信图
- 描述面向消息的动态行为

- 目的
- 对控制流进行建模
- 描述对象结构和控制的协调
- 状态图
- 描述面向事件的动态行为,可以描述一个对象的生命周期

- 圆角长方形代表一个个状态,箭头从一个长方形指向另一个长方形代表从一个状态跃迁到另外一个状态,实心圆形的代表初始状态,牛眼圆形代表最终状态
- 目的
- 为对象生命周期进行建模
- 为响应对象建模(用户界面、设备等)
- 活动图
- 描述面向活动的动态行为

- 实心圆,牛眼型圆也是代表活动的起始状态,圆角长方形代表一个状态,并不需要事件触发,菱形代表判定,粗实线横线代表分叉或合并,经常成对出现,分叉和合并之间代表着并发执行
- 目的
- 为业务工作流建模
- 为操作建模
4.软件架构建模--Rational的4+1模型
- 本质上和Kruchten的4+1模型一样,只是给了每个视图不同的名字

- 不是所有系统都需要所有视图
- 单一处理器:舍弃部署视图
- 单一进程:舍弃交互视图
- 小程序:舍弃实现视图
- 添加视图:
- 数据视图、安全视图
- 用例视图
- 包括用户、分析师、和测试工程师看到的系统行为的用例
- 根据视图可确定系统架构
- UML
- 静态方面由用例图描述
- 动态方面由交互图、状态图和活动图描述
- 设计视图
- 设计视图包括构建系统的类、接口和类之间的协作
- 主要支持系统的功能性需求,也即系统提供给用户的服务
- 静态方面由类图、对象图描述
- 动态方面由交互图、状态图和活动图描述
- 交互视图
- 交互视图描述了系统不同部分之间的控制流,包括可能的并发和同步机制
- 主要解决系统的性能、可扩展性、吞吐量等问题
- UML
- 静态方面和动态方面的描述所采用图都和设计视图相同
- 实现视图
- 实现视图包含用于组装和发布物理系统的组件
- 主要解决系统发布的配置管理问题
- UML
- 静态方面用物件图描述
- 动态方面用交互图、状态图和活动图描述
- 部署视图
- 部署视图包含形成系统硬件拓扑结构的节点
- 主要解决构成物理系统的部件的分布、发布和安装问题
- UML
- 静态方面用部署图描述
- 动态方面用交互图、状态图和活动图描述
5.设计原则--不良设计的特征
-
耦合:描述了一个对象依赖于另一个对象的程度。松耦合的对象可以独立发生变化,彼此不会互相影响。紧耦合的对象稍作修改则会导致一系列问题
- 紧耦合问题
- 修改一个模块会强制修改其他模块
- 模块难以单独理解
- 模块难以重用或独立测试,因为必须包含其所依赖的模块
- 继承关系是一种强依赖
- 紧耦合问题
-
不良设计特征
- 僵硬性
- 软件难以修改
- 系统的每一次局部修改都会迫使系统其他部分发生修改,系统是僵硬的难以变化的
- 如果单个模块的修改导致其依赖模块的级联更改,则设计是僵硬的
- 需要修改的模块越多,设计越僵硬
- 原因
- 模块化不好,耦合度高,紧耦合,模块间依赖程度很高
- 脆弱性
- 修改之后,别的模块容易出问题
- 更改会导致系统其他不相关的地方出现问题
- 与僵硬性密切相关(原因相同)
- 管理人员或开发人员会害怕变化
- 越脆弱的软件系统,维护越困难
- 不可移植性
- 可重用性差,不能重用到别的系统
- 难以将系统分解为重用与其他系统的组件
- 因为模块设计时考虑要重用。与低内聚有关
- 例如当模块依赖于系统基础结构或模块太专门化时
- 后果:软件只能重写不能重用
- 粘滞性
- 发生需求变化的时候,对软件修改非常困难
- 做正确的事情比做错事更困难
- 两种形式
- 设计的粘滞性或环境的粘滞性
- 如果修改时保留原有设计比破坏原有设计更难,那么设计的粘滞性会很高
- 当开发环境效率低下时,环境粘滞性就会产生
- 不必要的复杂性
- 存在没有必要存在的结构
- 设计包含的一些结构不具有价值
- 当开发人员预测未来的需求变化并为这些潜在变化进行设计时,这种情况经常发生
- 这些设计冗余的,并可能使系统的修改变得困难
- 不必要的重复性
- 重复性代码
- 不必要的重复性是代码复制粘贴的结果
- 所有的重复都不好
- 尤其半重复的代码更糟糕
- 软件难以维护
- 解决方法
- 抽象
- 重用
- 不透明性
- 可读性差,难以理解,不利于开发团队的交流
- 原因
- 不遵循编码标准
- 错误或不一致的命名
- 糟糕的注释或缺乏注释
- 模块太大
- 解决方案
- 代码编写时,进行代码审查以避免不透明的代码
- 模块大小要设计好
- 僵硬性
- 产生原因
- 需求总是变化的
- 糟糕的软件设计
- 短视的设计
6.1设计原则--单一职责原则(SRP)
-
软件设计原则时设计的指导方案,原则的遵循可以避免出现糟糕的设计
- 内聚性
- 衡量一个模块中的源代码行如何协调工作以提供特定功能的指标。面向对象编程中,内聚性指方法实现单一功能的程度。实现单一功能的方法具有高内聚性
- 低内聚性的表现
- 一个类的方法很少相关
- 方法执行不相关的活动,通常使用不相干的数据集
- 低内聚的缺点
- 模块理解困难
- 系统维护困难
- 模块重用困难
- 高内聚的原则可以应用于不同层面。
- 可以是方法、包和子系统层面的
- 单一职责原则(SRP)
- 每个类的职责应该是单一的
- 一个类只能有一个改变的理由
- 职责=“改变的理由”
- 如果一个类有多个职责,那么职责就会彼此耦合
- 如果模块具有多个职责,内聚性就低
- 良好的设计内聚性应该很高
- 例子

- 问题draw()和area()耦合在一个类里,Rectangle两个方法发生变化会影响依赖于它的两个类
- 修改

- Rectangle两个方法创建在两个不同的类中,一个类只实现一个功能
- 但是职责不会读变化的时候,职责无需分离。否则会造成“不必要的复杂性”
6.2设计原则--开放封闭原则(OCP)
- 开闭原则(OCP)
- 对扩展开放,对更改关闭
- 当需求发生变化时,应该以扩展的方式应对这个需求变化,而不是以修改源代码的方式去应对需求变化
- 实现
- 对可能发生变化的部分进行抽象
- 使用多态来添加不同的行为
- 抽象也可以应用于非面向对象(OO)语言
- 例子

- 如果BusinessClient具有对接口的引用,则替换
- 例子二

- 对扩展开放——为新的shaoe增加一个新的子类
- 对更改封闭——无需修改
- 满足开闭原则的代码基本不存在不良设计特征
- 当然,对所有的需求都封闭也是不切实际的,要根据需求变化真实发生时在做扩展性设计
- 刚开始时不做任何扩展性设计,发生需求变化时把这部分代码修改为扩展性设计对于未来发生需求变化的时候就可以对这种需求进行封闭了,要尽早刺激需求变化发生,发生阶段越早成本越低
- 刺激需求变化
- 先写测试
- 使用较短的开发周期
- 先开发核心功能并经常向涉众展示这些功能
- 首先开发最重要的功能
- 尽早经常发布软件
7.1设计原则--接口分离原则(ISP)
- 接口分离原则(ISP)
- 胖接口导致胖类
- 主要为了避免创建胖接口,主要解决胖接口问题
- 客户不应该被迫依赖其不使用的方法
- 消除多职责的类(接口不内聚的类)
- 将“胖”接口分解成多个接口,每个接口服务特定类型的客户程序
- 例子

- “胖”类接口应该分解为特定于不同客户程序的多个接口
- 客户程序不再依赖其不调用的方法,并且客户程序彼此独立,不再因为“胖”类耦合在一起
7.2设计原则--包设计原则
-
包是什么
-
包用来对类分组
-
系统中,有些类互相依赖即互相协作完成特定功能
-
系统中,有些类不知道彼此
-
-
将有关系的类放在一个包很自然
-
什么样的类应该放在一个包里
-
包之间的依赖关系是什么样的
-
-
包的6个设计原则
-
包的内聚性原则
-
重用发布等价原则(REP)
-
重用的粒度与发布的粒度等价
-
一起重用的类应该一起发布
-
类与类彼此依赖
-
-
共同重用原则(CRP)
-
包中的类一起重用,如果你重用包中一个类,则要重用包中所有的类
-
一个包的类彼此依赖
-
没有关系的类不应该放在同一个包中
-
-
共同封闭原则(CCP)
-
一个包中所有的类应该对同一种需求变化封闭。影响包的变化影响包中所有的类,但不影响其他包(跟功能相关的所有类都放在一个包里)
-
一个包只有一个变化的原因
-
-
-
耦合度原则
-
无环依赖原则(ADP)
-
在包依赖关系图中不允许环存在
-
解决早后综合征
-
模块和模块间不兼容
-
解决方案
-
每周构建
-
所有的开发人员在每周的前四天工作与自己的代码库,不需要对彼此的代码集成
-
周五整合开发的所有代码并建立系统
-
只适用于中型项目
-
-
ADP
-
将开发环境划分为可发布的包
-
为了采用自底向上增量式开发,不允许环存在
-
有向无环图
-
无论从哪个包开始,都不可能沿着依赖回到起点包。这种结构没有环
-

-
解除环并将依赖关系图恢复为DAG的两种方案
-
应用依赖倒置原则
-

-
创建一个MyDialog和MyApplication都依赖的新包
-
-
-
-
-
-
-
包结构随着系统的增长而演化
-
包依赖关系无法在类设计之前进行
-
-
稳定依赖原则(SDP)
-
顺着稳定的方向依赖
-
SDP可以确保不稳定的模块依赖稳定的模块
-
用户界面不稳定,数据库稳定
-

-

-

-
SDP表示,包的I值应该大于它依赖的包的I值(即I顺着依赖的方向降低)
-
-
稳定抽象原则(SAP)
-
稳定的包应该是抽象的
-
一个稳定的包英阿共是抽象的,其稳定性不应该妨碍它被扩展
-
SAP和SDP合起来相当于包的DIP原则
-

-
-
-
8.1里氏替换原则(LSP)
- 里氏替换原则(LSP)
- 保证类的继承是一个良好的继承
- 关键
- 通过继承实现抽象和多态性
- 子类型必须可以替代其基类型
8.2依赖倒置原则(DIP)
- 依赖倒置原则(DIP)
- 高层次模块不应该依赖底层模块,两者都应该依赖抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 产生原因
- 结构化分析与设计往往倒置高层模块依赖低层模块,策略依赖细节实现
- 高层模块一般包括应用程序的商业决策和业务模型
- 例子
- DIP原则核心是应该只依赖抽象,即抽象类和接口
- 这种假设过于严格,对于不容易变化的类,不需要使用抽象
- 抽象应该用到系统的易变化部分,以及需要进行解耦的部分
9.质量属性
- 通用质量属性场景
- 通用质量属性场景具有系统独立性,适用于任何系统
- 具体质量属性场景
- 具体质量属性场景特定于特定系统
- 具体质量属性场景让质量需求变得
- 可用性的通用场景
- 可修改性的通用场景
- 具体场景的特征
- 具体场景集合可用于做系统的质量属性需求
- 每个场景必须足够具体才对架构师有意义
- 响应细节足够具体以便测试系统是否满足了质量属性
- 质量性需求应该在需求分析期间获得,但实际上很少能做到
- 通过生成质量属性场景来缺点质量属性需求师架构师的任务
- 特定质量属性表可用来创建通用场景并进而确定具体场景
- 质量属性场景应用
- 质量属性表可充当核对清单,以确保所有可能性都被考虑到
- 从不同质量属性可能会生成相同或相似的场景,冗余可容易被删除
- 重要的是灭有重要的需求被遗漏
- 质量属性场景
- 通用场景提供了生成大量独立于系统的特定质量属性场景
- 系统相关的特定质量属性的具体场景集合构成该系统的特定质量属性需求
13.软件体系风格--管道过滤器模式
- 管道过滤器模式由一系列处理单元(过滤器)组成,每个单元的输出是下一个单元的输入。在连续的单元之间通常提供一定的缓冲

- 每个过滤器的耦合度非常低,可用随时被替换,内聚性非常高
- 组件
- 过滤器——处理数据
- 从输入读取数据流并输出数据流
- 过滤器——处理数据
- 连接器
- 管道——数据翻译与传输
- 将过滤器的输出数据传送给其他过滤器
- 管道——数据翻译与传输

- 拓扑结构——线性
- 变种
- 反馈循环,分裂管道

- 变种
- 语义约束
- 过滤器是独立实体
- 他们不共享状态
- 他们不知道其前任/后继
- 过滤器是独立实体
- 管道过滤器模式的优缺点
- 优点
- 高内聚——过滤器是执行特定功能的自包处理服务,具有较强的内聚性
- 低耦合——过滤器间仅通过管道通信
- 可重用——支持过滤器的重用
- 可简单地实现为并发或顺序系统
- 可扩展性——容易添加新的过滤器
- 灵活性——过滤器功能可重新定义,线路可改变
- 缺点
- 管道中数据传输需要公共格式
- 难以支持基于事件的交互
- 优点
15.1软件体系结构风格--客户服务器架构模式
- 组件
- 客户端
- 对服务器发出请求并处理系统环境中的输入/输出
- 服务器
- 响应客户请求的应用程序
- 服务器不知道客户端的数量和身份
- 客户端知道服务器的身份
- 客户端
- 连接器是基于RPC的网络交互协议
- 为什么是客户端-服务器架构?
- 多个用户想要共享和交互数据
- 典型应用领域
- 分布式多用户(企业)信息系统

- 依赖关系
- 客户依赖服务器
- 拓扑结构
- 一个服务器可能会链接一个或多个服务器
- 客户端间没有连接
- 移动性
- 客户端可移动
- 安全性
- 通常在服务器端控制,也可能在应用程序/业务层实现
- 一层或两层客户端-服务器架构
- 客户端和服务器都在一台机器上叫一层的客户端/服务器架构,表示逻辑和应用逻辑都在客户端或服务器是两层
- 客户端有应用程序逻辑的叫胖客户端,没有的叫瘦客户端
- 瘦客户端的优点
- 网络负载低
- 不需要维护
- 胖客户端
- 网络负载高
- 配置管理复杂
- 安全性复杂
- 稳健性
- 客户端出现问题=>复杂的故障恢复
- 客户端-服务器架构特征
- 优点
- 有效使用网络系统;只需要更便宜的硬件
- 易于添加新服务器或升级现有服务器
- 允许多用户间数据共享
- 可扩展:添加新的客户端
- 缺点
- 每个服务器上的冗余管理
- 很难发现可用的服务器和服务
- 很难改变客户端和服务器的功能
- 如果程序逻辑在客户端-服务器上分布,改变程序逻辑很困难
- 可扩展性受限于服务器和网络容量的限制
- 优点
- 三层客户端-服务器架构
- 展示层
- 负责显示用户界面的层
- 业务层
- 分为两层
- 业务逻辑层
- 在数据访问层之上,即业务逻辑层用到数据访问层的类和对象
- 数据访问层
- 负责处理数据并将数据传到业务逻辑层
- 业务逻辑层
- 业务层负责访问数据层来获取、修改、删除数据并将结构发送到展示层
- 分为两层
- 数据层
- 数据层指的是数据库或者数据源
- 常见错误
- 技术上层与层之间紧密耦合
- 在表示层写业务逻辑
- 与两层客户端你-服务器架构相比较的优点
- 更好的性能
- 更好的扩展性,可重用性和可维护性
- 与安全性相关的措施可以集中分配
- 不同层可以平行开发
- 展示层
16.软件体系结构风格--分层架构
- 分层系统
- 构件:层
- 连接器:层间的交互协议
- 层级组织结构
- “多层客户端-服务器”
- 每层暴露一个接口(API)以被上层调用
- 每层可充当
- 服务器:给上层提供服务
- 客户端:下层服务的消费者
- 分层架构
- 每层提供一组相关的服务
- 每层只使用下面的层
- 如果任意一层只依赖与其直接相邻的下一层,那么它是严格分层结构
- 如果一层依赖下面的任意一层,那么它是宽松分层结构
- TCP/IP也是分层架构

- 优点
- 每层都提供一系列服务,层的内聚性高
- 每层都对其他层隐藏私有信息
- 每层只用到更低级的层,以限制耦合度
- 容易对当前层进行替换和修改
- 对某层的修改最多影响相邻的两层
- 每层都是内聚的,只和低层耦合因此它很容易被重用、替换或互换
- 数据库的变化只会影响数据存储/访问层,浏览器的变化只会影响展示层
- 只要接口不变,每层可有不同的实现
- 缺点
- 严格的分层可能导致性能问题,具体取决于层数
- 建立清晰的分层结构并不总是很容易
17.软件体系结构风格--MVC架构
- M(model) V(view) C(controller)

- 三个组件间的耦合度非常低,需求发生变化时只会影响局部
- 优点
- 视图,控制器,模型是独立的组件,允许在每层内部进行修改和变化而不干扰其他层
- 视图组件经常需要改变(UI技术的改进)和更新来保证用户持续的兴趣。视图组件时独立的
- 即使模型层停止工作,视图-控制器也能保持部分功能
- 缺点
- 严重依赖于与MVC架构相匹配的开发和生产系统环境和工具
19.软件体系结构风格--微服务架构
- 什么是微服务架构
- 微服务是一种架构风格,将应用程序组织为小的自治服务集合,围绕业务领域建模,其中大型复杂软件应用程序由一个或多个服务组成。
- 在微服务种,每个服务都是独立的,实现单一业务功能。微服务可用彼此独立地部署并且耦合松散。每个微服务都专注于完成一项任务
- 微服务架构,简称微服务,是专注于构建具有良好定义的接口和操作的单一功能模块
- 微服务的好处
- 部署容易
- 理解容易
- 缺陷隔离更快速
- 变化风险最小化

21.1创建型软件设计模式——工厂模式
- 工厂模式可能是现代编程语言(Java/C#)中最常用的设计模式
- 目的
- 创建对象的过程中没有暴露对象的初始化逻辑给用户
- 通过一个公共接口引用最新创建的对象
- 分类
- 工厂方法
- 抽象工厂

- +FactoryMethod()根据外部传递来的参数动态的创建它应该创建的产品
- Product定义工厂方法要创建的对象的接口
- ConcreteProduct实现Product接口
- Creator声明工厂方法,返回一个产品类型的对象,构造器也可能定义一种缺省的工厂方法实现,返回一个缺省的ConcreteProduct对象
- 可能会调用工厂方法创建一个产品对象
- ConcreteCreator重写工厂方法来获得一个ConcreteProduct的实例

- 工厂模式使用场景
- 当类不能预测要创建哪个类的对象的时候
- 当类应用其子类去定义要创建的对象的类型时
- 当对象的创建要局部化时
- 工厂模式的几个变种
- 基类是抽象类,模式必须返回一个完全工作类
- 基类包含缺省方法,在缺省方法无效时被子类继承
- 通过传递不同参数给工厂从而返回不同类型的类实例。这种情况下,这些类共享同一个方法命,但方法实现不同
21.2创建型软件设计模式——抽象工厂模式
- 定义
- 定义了创建一系列相关的或依赖的对象的接口,没有规定其具体的类
- 抽象工厂时一个可以创建其他工厂的超级工厂是工厂的工厂

- 抽象工厂是工厂的工厂
- AbstractFactory——定义一个创建产品的操作接口
- ConcreteFactory——实现创建具体产品对象的操作接口
- AbstractProduct——声明一类产品对象的接口
- Product——定义由相应ConcreteFactory创建的产品对象
- 实现抽象产品接口
- Client——用户接口类
- 用户进行交互的程序
21.3创建型软件设计模式——单例模式
- 单例模式从 某种程度而言属于“非创建”模式,但是依然将其和其他创建型设计模式放在一起
- 目的
- 保证每一个类只有一个对应的实例被创建
- 保证一个指向这一单一实例的全局指针
- 一个类只能创建一个实例,并且该实例可被全局访问

21.4创建型软件设计模式——建造者模式
- 建造者模式定义
- 将复杂对象的构造与表示分开,以便相同的构造过程可以创建不同的表示

- Builder——定义抽象接口创建Product对象部分
- ConcerteBuilder
- 通过实现Builder接口来构建和组装产品的部分
- 定义并跟踪其创建的复杂对象表示
- 提供用于获取产品对象的接口
- Diretor——使用Builder接口构建对象
- Product——代表正被创建的复杂对象。ConcreteBuilder构建了产品的内部表示并定义了产品的组装过程

- 建造者模式的合作过程
- 客户程序创建Diretor对象并使用所需的Builder对象对其进行配置
- 当产品的一部分需要被创建时,Diretor通知Builder
- Builder处理来自主管的请求将产品part添加到产品中
- 客户程序从Builder获取产品
- 创建者模式的特点
- 创建者可以改变其构建的产品内部表示,并隐藏产品对象的组装细节
- 每个特定的创建者独立于其他创建者和程序的其他部分。这提高了程序的模块性和可扩展性
- 每个创建者根据数据逐步构建最终产品对象
- 建造者模式vs抽象工厂模式
- 相同点
- 两种模式都返回由多个方法和对象组成的类
- 不同点
- 抽象工厂返回一系列相关的类;建造者根据呈现给其的数据构建一个复杂对象
- 相同点
21.5创建型软件设计模式——原型模式
- 当创建一个类的实例非常耗时耗力或者非常复杂时,可使用原型模式:创建原型实例发副本并适当对其修改
- 定义
- 定义可通过原型实例创建的对象类型
- 通过复制原型创建新的对象

- Prototype——声明用于克隆自身的接口
- ConcretePrototype——实现克隆自身的操作
- Client——通过让原型克隆自身创建新对象
- 克隆方法
- 利用clone方法创建任意java对象的副本
- Job j1=(Jobj)jo.clone();
- 克隆方法的四个克制
- 克隆方法总是返回一个Object类型的对象,因此必须强制类型转换为正在克隆对象的实际类型
- 该方法为protect类型方法,只能在类内部或者包含该类的包内调用
- 只能克隆实现了Cloneable接口的对象
- 不能被克隆的对象会抛出CloneNotSupported异常
22.1结构型软件设计模式——适配器模式
- 使一个类和接口不匹配的其他类进行交互
- 意图
- 将一个类的接口转换为client类期望的另一个接口
- 适配器让类可以协作工作,否则会因为接口不兼容无法进行
- 两种适配器
- 类适配器——通过继承实现
- 从不兼容的类派生出新类并且添加我们需要的方法使得派生类满足预期接口

- 对象适配器——通过对象组合实现
- 新类包含原始类并且在新类里创建方法以实现调用的转换

- Target——定义client使用的特定于某个域的接口
- Adapter——将Adaptee接口适配到Target接口
- Adaptee——定义需要被适配的已有接口
- Client——与遵循Target接口的对象协作
- 类适配器——通过继承实现
22.2结构型软件设计模式——组合模式
- 目的
- 将对象组织成树结构以表示“部分——整体”的层次结构
- 组合模式让client程序以统一的方式对待单个对象和组合对象

- 参与者
- Client——使用Component接口操作层次结构中的对象
- Component——使Leaf节点和Composite节点的抽象,它定义了组合对象中必须实现是接口
- Leaf——没有子节点的对象,其实现由组件接口声明的服务
- Composite——存储子组件,实现Component接口定义的方法。除了实现Component接口定义的方法。此外,Composite提供添加、删除以及获得组件Component的方法
22.3结构型软件设计模式——外观模式
- 为子系统的接口集合提供统一的接口。外观定义了子系统更易于使用更高层次的接口
- 子系统或子系统集合的类暴露的接口可能很复杂
- 降低复杂性的方法是引入外观对象,它为子系统的通用功能提供单一的、简化的接口


- 参与者
- Facade——了解哪个子系统复杂哪个请求,将客户端请求委托给合适的子系统对象
- Subsystem Class
- 实现子系统功能
- 处理Facade对象分配的工作
- 不知道facade对象,没有引用favade对象
- 什么时候使用外观模式
- 为复杂的子系统提供简单的接口。此接口对于大多数客户程序来说足够好,更复杂的客户程序可以调用子系统接口
- 将子系统的类与其客户程序和其他子系统解耦,从而提升子系统的独立性和可移植性
- 外观模式的好处
- 向客户隐藏了子系统的实现,使得子系统更易于使用
- 降低了子系统和其客户程序之间的耦合度
- 减少了大型软件系统中的编译依赖关系
- 结果
- 使一直系统到其他平台变得更简单
- 不会阻止复杂客户程序访问底层类
- 注意Facade类没有添加任何功能,只是简化了接口
- 他不会阻止客户程序访问底层类,应该要尽量避免
23.1行为型软件设计模式——迭代器模式
- 迭代器模式是设计模式中最简单,最常用的模式之一
- 迭代器模式允许使用标准接口来遍历列表或数据集合,不必知道数据的内部表示
- 可以定义执行特殊处理的特殊迭代器,只返回数据集合的特定元素
- 目的
- 提供一种顺序访问集合对象的元素而不暴露其底层表示的方法
- 动机
- 在不暴露集合内部结构的情况下访问集合的内容
- 实现
- 它支持对于一个集合的并发遍历
- 为遍历不同的集合提供统一的接口
- 允许不同的遍历方法
- 不应该将这些方法添加到集合的接口中

- Iterator——定义访问和遍历元素的接口
- ConcreteIterator——实现Iteretor接口,跟着遍历集合中的当前元素
- Aggregate——定义用于创建迭代器对象的接口
- ConcreteAggregate——实现迭代器创建接口以返回适当的ConcreteIterator的实例
- 适用性
- 支持集合对象的遍历而不公开他们的内部表示
- 支持集合对象的多个、并发的遍历
- 为遍历不同的集合结构提供统一的接口
- 好处
- 将集合遍历方法从集合类中分离简化了集合接口
- 支持多个并发遍历
- 支持变种遍历技术
23.2行为型软件设计模式——状态模式
- 目的
- 允许对象在其内部状态改变时改变其行为,看起来改变了它的类

- 参与者
- Context
- 定义了client感兴趣的接口
- 维护定义当前状态的ConcreteState子类的实例
- State——定义了用于封装与Context的特定状态相关联的行为的接口
- Concrete State——每个子类实现与Context状态相关联的行为
- Context
- 状态模式应用场景
- 对象的行为取决于它的状态,并且必须根据该状态在运行时改变其行为
- 对象方法中具有依赖对象状态的大量if-else等条件选择语句的时候。状态模式将条件的每个分支置于单独的类
- 好处
- 将与状态相关联的所有行为都封装于一个对象在
- 允许状态迁移逻辑封装到状态对象中而不是用大量if-else语句
- 坏处
- 对象数量增加
23.3行为型软件设计模式——策略模式
- 为什么需要策略模式
- 程序Context中封装了许多相关的算法。Client程序可以选择这些算法之一,胡总某些情况下,Context可能为Client选择最佳的算法,比如选择不同方式绘制相同数据:线图、条形图或饼图
- 目的
- 定义一系列算法,对每个算法进行封装并使它们可互换
- Strategy使算法与Client程序解耦

- 参与者
- Strategy——为所支持的算法声明一个公共接口。Context使用此接口调用由ConcereteStrategy定义的算法
- ConcereteStrategy——遵循Strategy接口,实现算法
- Context
- 配置ConcereteStrategy对象
- 维护对Strategy对象引用
- 可以定义允许Strategy访问其数据的接口
- 策略模式的适用性
- 许多相关的类只在行为上不同
- 算法有不同的变种
- 算法使用client不知道的数据。使用策略模式可避免暴露复杂的,特定于算法的数据结构
- 一个类定义了许多行为,这些行为在其操作中作为多个条件语句出现。代替许多条件句,将相关的条件分支移到相应的Strategy中
23.4行为型软件设计模式——命令模式
- 目的
- 将服务请求封装成对象,从而可以从不同的方式对请求进行操作
- 它使client可以调用命令但不需要了解命令的执行细节,并且可以对命令进行修改而无需影响调用命令的client程序

- 参与者
- Command——声明执行操作的接口
- ConcereCommand
- 定义Receiver对象和动作的绑定
- 通过调用Receiver的相应操作实现Execute()方法
- Client——创建ConcereCommand对象从而设置对应的receiver
- Invoker——要求命令执行用户请求
- Receiver——负责解析用户的请求并执行相应的操作
- 命令模式总结
- 命令模式实现了调用操作的对象与具体实现操作的对象之间的解耦
- 命令可以向其他对象一样被操作和扩展
- 命令可以被组合成一个复合命令
23.5行为型软件设计模式——中介者模式
- 为什么需要中介者模式
- 程序中出现越来越多的彼此分离的类,这些类之间的通讯变得越来越复杂
- 程序越来越难以阅读和维护
- 中介者模式通过降低这些类之间的耦合度来解决这一问题
- 目的
- 将对象的交互封装在一个中介者对象中。中介者使各对象不需要显示地相互引用,从而使其耦合松散,而且可以实现独立改变它们之间的交互

- Meadiator——定义Colleague类之间交互的接口
- Concrete Meadiator
- 实现Colleague对象之间的交互协作
- 维护其Colleague类
- Colleague——定义和Mediator类之间通讯的接口
- ConcrateColleague
- 每个Colleague类都知道其中介者
- 每个Colleague类要和另一个Colleague类进行通讯时,都要中介者
- 优点
- 中介者降低了程序中类之间的耦合度
- 可以通过改编自中介者类或实现其子类来改变程序的行为
- 中介者模式允许系统添加新的Colleague而无需改变程序的其他部分
- 中介者模式解决了每个对象都要充分了解其通讯对象才能进行通讯的问题
- 中介者模式总结
- 便于理解——中介者封装了Colleague之间的交互逻辑。因为所有交互逻辑都封装在一个类中,便于理解
- 解耦Colleague类——Colleague类之间完全解耦,添加一个Colleague类变得容易
- 简化了对象协议——Colleague对象只需和中介者对象交互,中介者模式有效地将多协议简化为一对多或多对一
- 限制子类数量——所有的交互逻辑都封装在中介者类中。当交互逻辑需要扩展时,只需扩展中介者类
- 缺点
- 中介者类会变得越来越复杂,难以改变和维护
- 很难在不同的项目中重用中介者类的代码
- 每个类都需要知道中介者的存在
23.6行为型软件设计模式——访问者模式
- 目的
- 将数据操作和数据结构分离
- 访问者可以定义新的操作,而不需要改变其操作对象的类

- Vistor
- 为对象结构里的每个ConcreteElement类声明一个Vistor操作
- 通过操作的名字和签名可以识别给Vistor发送Vistor请求的类。这样Vistor可以确定被访问的元素的具体类,然后Vistor可以直接通过特定接口访问元素
- ConcreteVistor——实现Vistor声明的每个操作,每个操作实现了为相应类或对象定义的算法片段。ConcreteVistor为算法提供了上下文并存储了它的本地状态。这个状态经常在结构遍历过程中积累
- Element——定义了以Vistor作为参数的accept()操作
- ConcreteElement——实现了接受Vistor作为参数的accept()操作
- ObjectStructure
- 可以枚举所有元素
- 可以提供高层次接口,允许Vistor访问它的元素
- 可以是组合或如同List或Set的元素集合
- 双重分发
- Vistor工作需要分发一个方法2次
- Vistor多态调用特定对象的accrpt()方法
- accept方法多态调用Vistor的visit方法

- 应用场景
- 需要对集合或更复杂数据结构里的不同类型对象执行类似操作
- 有许多不同的不相关操作需要执行。访问者模式允许为每个操作类型创建一个ConcreteVistor类,把操作实现与对象结构分离开
- 一般,对象的数据结构不大可能变化但可能需要添加新操作
- 访问者模式将ConcreteVistor(操作,算法,行为)和对象数据结构分离。只要结构保持不变,很容易添加新的Vistor
- 优点
- 添加新操作很容易
- 对象的相关操作行为被封装进Vistor者类,没有分散在定义对象结构的类里
- 缺点
- 添加新的ConcreteElement类苦难。每个新的ConcreteElement都会在Vistor基类添加一个新的抽象方法,并在ConcreteVistor类中添加一个相应的实现
- ConcreteElement接口必须提供公共操作访问元素的内部状态,这会破坏对象的封装性




939

被折叠的 条评论
为什么被折叠?



