6.1 分布式物理体系结构
(Distributed physical architecture)
1、主要目标是降低耦合
2、体系结构设计(具有物理和逻辑两个方面)
- 物理体系结构
- ——解决C/S 客户机/服务器问题,以及CS之间所需要的任何中间件的问题
- 使用结点和部署图
- 逻辑体系结构
- 客户机client:发起请求
- 服务器server:处理客户端发来的请求
6.1.1 对等体系结构(P2P)
1、好处:
- 没有管理结点,使网络负载得到均衡
- 对于网络故障和单个同位体故障,对等系统易于快速恢复,且易于扩展和适应
2、坏处:在对等网体系结构中,当总的系统吞吐量达到最大时,就要考虑网络流量最小化的问题。确保对等系统具有一致的反应时间是一种挑战。
3、主要用在分布式的文件共享系统(六维空间)
4、系统中任何结点可能既是客户机,也是服务器
6.1.2 分层体系结构
1、为了降低耦合
2、像同位体的情况一样,层次结构中间的每一层既是客户机也是服务器
3、划分出来三层体系结构:
- GUI客户机
- 业务逻辑中间层
- 数据库服务器
4、分层体系结构不同于对等体系结构,因为它在硬件层和软件层之间采用了层次依赖方法,从本质上来讲这种依赖与逻辑体系结构框架的层次非常一致(例如J2EE、PCBMER)
- 当应用逻辑被编译到客户机时——胖客户体系结构“ a client on steroids”
- 当应用逻辑被编译到服务器时——瘦客户体系结构“ a skinny client”
- 一部分应用逻辑被编译到客户端,一部分被编译到服务器——中介者体系结构
6.1.3 数据库为中心的体系结构
-
数据库程序被称为存储过程(触发器),可以从客户端程序调用它
-
一种特殊的存储过程(触发器)不能被显示地调用。当试图更改数据库内容时,触发器自动触发
-
需要考虑的编程部分:
-
用户界面(客户端):知道如何以特定的GUI显示信息,如web浏览器
-
表示逻辑(客户端):按照应用程序功能的需求负责处理GUI对象
-
应用(控制)功能:包括程序的主要逻辑
-
完整性逻辑(数据库责任):负责企业范围的业务规划(触发器编程手段)
-
数据访问(数据库责任):知道如何访问磁盘上的持久数据
-
6.2 多层逻辑体系结构
1、(Mulitilayer logical)
2、成功的系统通过层次结构hierarchies组织,将指数级复杂度降低成多项式级
6.2.1 体系结构的复杂性
1、两种主要的、有差别的计算模型是基于算法的图灵机模型和基于算法和开放的交互模型
2、复杂性的4种解释:
-
问题复杂性(计算复杂性):由问题域本身复杂性定义(软件本质特性的一个分支)
-
算法复杂性:目标是度量算法的效率
-
结构复杂性:目标是建立软件结构之间的关系及易于维护和演化,即降低耦合度的问题
-
认知复杂性(Cognitive complexity):度量理解软件做出的努力,即捕获程序的逻辑流,并度量逻辑流的各种特性
注:认知复杂性度量可以增强可理解性度量,结构复杂性度量可以增强可维护性和可伸缩性度量
3、空间认知复杂性——度量软件工程师能看懂代码目光所移动的代码的行数
(measure the distance that a software engineer must move within the code to build a mental model of it. )
4、结构复杂性:强调程序对象之间的依赖关系,尽量减少依赖
- 如果系统中所有的依赖都被标识和理解,则系统具有可适应性(可理解、可维护、可扩展)
- 网络的结构复杂性:
- 每条通信路径允许类之间的双向交互——从B到A和从A到B
- 计算公式: net CCD=n(n-1)
- 层次的结构复杂性:
- 强调层之间的层次体系分解,而在层的内部则允许类似网络的交互
- 通过一系列分层原则,如PCBMER中的DDP(向下依赖)原则,即层之间的通信路径是向下的
- 计算方法:设层l1,l2,l3,…,ln.对于任一层li,令
- size(li)为第li层的对象数
- li为第li层的双亲数
- pj(li)为第li层的第j个双亲
6.2.2 体系结构模式
在介绍PCBMER体系结构框架时我们列出了框架必须遵守的几项原则,这些原则在任何具体的体系结构设计中的实现需要遵循具体的设计模式
6.2.2.1 外观(facade)
1、定义:使子系统更易于使用的高层接口
2、目标:减少子系统之间的通信和依赖性
2、副作用:层中的类隐藏在外观对象的后面,需要更复杂选择的客户仍然可以直接与层中的类通信
3、使子系统更易于使用的高层接口,这个高层接口可以是具体类(域类)或抽象类
4、关键问题:是高层接口封装了高层(子系统,包)的主要功能,并向该层的客户提供主要的甚至是唯一的入口点。一般情况下,一个层会针对更高层的不同客户定义多个外观。
6.2.2.2 抽象工厂(Abstract Factory)
1、提供一个接口,创建相关的或依赖的对象,而不需要说明它们的具体类
2、是外观模式的变种在外观模式中“高层接口”将意味着一个具体类,
而抽象工厂中的接口或者是一个真正的接口,或者是一个抽象类
3、有益于增强应用系统的整体灵活性。这种灵活性在设计期间和运行期间都有体现
4、可以将抽象工厂当作一种“高级接口”使用,通过此接口引导与包的通信,并将包中的实际工作的类封装起来。
5、这种抽象工厂框架使得增加新的活动很容易,只需要创建一个新的具体产品的类即可,
对于每一种新增加的活动,我们只需要定义额外的:
- 具体工厂类、
- 一个匹配的抽象产品接口
- 任何具体的产品类
6.2.2.3 责任链(chain of Responsibility)
1、目的:通过将处理问题的机会给多个对象来避免问题的发送者和接受者之间的耦合
2、可以把责任链看成是委托概念的变体,可以理解为消息的引用类
3、用双亲-子或容器-被包含模型实现,如果子对象没有能力处理该消息,它将返回给双亲或者是双亲的双亲,直到找到一个合适的处理对象
4、在PCBMER中,责任链使得==NCP(相邻通信原则)==成为可能
6.2.2.4 观察者(observer/listener)
1、也称为出版-订阅模式
2、目的:定义对象之间的一对多依赖,使得当一个对象变更状态时,其所有依赖者都被通知到并自动更新
3、此模式关系到两类对象:
- 被观察对象:称为主题或出版者
- 观察对象:称为观察者、订阅者或监听者
4、一个主题可以由很多观察者订阅他,主题状态的变更会通知到所有的观察者,然后所有的观察者都会执行必要的处理,使他们的状态与主题的状态同步。观察者之间没有相互关联,并且可以做不同的处理来响应主题状态变更的通知,如全家人盯着一条刚买的不听话的狗
6、对应的相反的模式——通知模式:由主动观察者去被动接收被观察者发来的消息,如:狗饿了向全家人叫唤
6.2.2.5 中介者(mediator)
1、定义类来封装其他类之间的相互通信。通过使对象之间的不显示引用来降低耦合,并允许独立地改变他们的交互
2、它允许我们将复杂的处理规则组织到专门的中介者类中,从而使其他复杂的对象不需要交换很多消息就可以做这种处理
6.3 体系结构建模
1、在UML中,实现建模的设施支持体系结构建模,实现模式是以结点、构件、包、子系统等概念为中心的。
2、除了实现模型,UML通过给类图增加设计约束支持体系结构的建模:采用在类及其他模型中可视化依赖关系
3、依赖是体系结构框架的基石,决定了体系结构的复杂性。
6.3.1 包
1、UML提供包的概念用于表示一组类(或用例)
2、包用于划分应用程序的逻辑模型
3、包是高度相关的类的聚合,这些类本身是内聚的,但对于其他聚合来说是松散耦合的
4、包可以嵌套(nested),外层包可以直接访问包括在它的嵌套包中的任何类。一个类只属于一个包
5、包之间的两种关联:泛化、依赖
-
从A到B的依赖说明:对B的变更可能需要A的变更
-
包之间的依赖很大程度上是由于消息传递,即一个包中的类发消息给另一个包中的类
-
包之间的泛化也意味着依赖,依赖是从子类包到超类包
6.3.2 构件
1、是系统的物理部分、实现的一个片段或一个软件程序
2、一般将构件理解成系统的二进制可执行部分(EXE),也可以是系统中不可直接运行的部分(如源代码文件、数据文件、DLL、数据库中的存储过程)
3、构件具有以下特性:
- 构件是独立的部署单元——不可以部署构件的一部分
- 构件是第三方组装单元——它是充分文档化和自包含的,可以被第三方插入到其他构件中
- 构件没有持久状态——不能与他的拷贝区分开,在任何给定的应用系统中一个特定的构件最多有一个拷贝
- 构件是系统的可替换部分——可以被符合相同接口的另一个接口替换
- 构件完成清晰的功能,并且逻辑耦合和物理耦合的
- 构件可以嵌套在其他构件中
6.3.2.1 构件与包
1、包是建模元素的分组,并具有指定的名字,在逻辑层上每个类都属于一个唯一的包
在物理层上每个类至少由一个构件实现,并且一个构件可能只实现一个类。抽象类和接口经常被多个构件实现
2、包是比构件更大的体系结构单元,倾向于以水平方式组织类——在应用域中静态接近的类
构件是对行为相近的类的垂直组织——这些类可能来自不同的域,但贡献于单一的业务活动
3、通常情况是逻辑包依赖几个物理构件
6.3.2.2 构件与类的接口
与类一样,构件也实现接口。其区别是双重的:
- 首先,构件是部署在某个计算机结点上的物理抽象,类表示逻辑事务,为了起到物理抽象的作用不得不将其实现为构件
- 其次,构件只显示它所包含的类的某些接口,很多其他接口都被封装在构件中,对其他构件不可见
6.3.3 结点
1、在UML中,分布式物理体系结构或系统的任何其他体系结构都被描述为部署图。
2、结点:部署图中的计算机资源,结点至少具有内存和某些计算能力
3、结点实际上可能是数据库服务器、计算机、客户端等
4、结点是构件得以运行的硬件位置,结点运行构件,构件被部署在结点上,将结点和部署在上面的构件称为分布单元
6.4 程序设计与复用原则
1、程序设计是整个系统设计的固有部分,体系结构设计建立通用的运行框架,GUI和数据库的详细设计明确说明框架的前端和后端。
- 架构设计——框架
- 详细设计——前界面,后数据库
- 程序设计——对界面设计和数据库设计的扩展
2、程序设计有时专注于一个应用程序,是第7章和第8章的扩展。
3、程序的运行逻辑是位于客户机进程和服务器进程之间分离的部分,客户机进程包括了程序中的绝大多数动态对象协作,服务器进程关系由客户机进程引起的执行业务交易。
6.4.1 类的内聚和耦合
(cohesion、coupling)
1、良好的程序设计可以确保类的内聚和耦合之间的良好平衡
2、类内聚是一个类内部自确定的程度,它测量类独立的强度,内聚越强越好
3、类耦合是类之间连接的程度,它测量类的相互依赖性,耦合越弱越好,但为了“协作”不得不耦合
4、内聚和耦合是彼此关联,更强的内聚导致更弱的耦合,需要在两者之间取得最好的平衡,
一些启发原则来维持平衡:
- 两个类或者不彼此依赖,或一个类只依赖另一个类的公共接口
- 属性和相关方法应该保存在一个类中,如学校注册只关注学生的学号姓名等属性(这项原则经常被破坏)
- 一个类应该只捕获一个对象,无关信息移到另一个类中
- 系统功能应该尽可能均匀分布
6.4.1.1 类耦合的种类
1、两个类为了通信,就要耦合,如果类X直接引用类Y,类X与类Y之间的耦合就存在
2、6种常见形式:
- X包含Y,或者X具有指向Y的实例的属性
- X具有引用Y的实例的方法
- X调用Y的服务(给Y发消息)
- X是Y的直接子类或非直接子类
- X具有输入参数是类X的方法
- Y是一个接口,而X实现了这个接口
6.4.1.2 Demeter法则
1、类耦合对于对象之间的通信是必要的,但是应该被限制在类的层次内,即层内耦合;
2、Demeter法则提供类限制类间任意通信的指南——如“不要和陌生人讲话”、“只与你的朋友讲话”等法则
3、Demeter法则说明了在类方法中允许什么样的消息目标,这些目标只能是以下对象之一:
- 方法的对象本身
- 方法型构中作为参数的一个对象
- 此对象的属性所引用的对象(包括属性的集合中所引用的对象)
- 此方法创建的对象
- 全局变量引用的对象
4、Demeter增强法则:为了限制继承带来的耦合,可以将第3条规则限制在类本身定义的属性上,此类继承来的属性不能被用于标识消息的目标对象
5、在实践方面,Demeter法则只是强迫执行良好的体系结构设计和体系结构框架原则的建议。对相邻层中的类应用这个法则,“NCP”原则得到实现
6.4.1.3 存取方法和机械类
(Accessor methods、mindless classes)
1、存取方法定义观察者(get)或改变者(set)操作
2、机械类:一个具有很多存取方法的类,也称为无思维类,由其它类决定该类状态
6.4.1.4 动态分类和混合实例内聚
1、Dynamic classification (If object can dynamically change its class) not supported ->mixed-instance cohesion
2、没有动态分类的支持造成了混合实例内聚,这是面向对象编程环境的弱点
3、带有混合实例内聚的类中的某些对象可能是未定义的,这时候可用泛化消除这个问题
6.4.2 复用策略
1、UML将复用定义为“已有人工制品的使用”。
2、软件复用的策略,策略还意味着复用的粒度,粒度可能是:
- 类
- 构件(component)
- 解决方案
3、与粒度相关联,有3种对应的复用策略:
- 工具包(类库)
- 框架
- 分析与设计模式
6.4.2.1 工具包复用
Toolkits(class libraries)
工具包强调在类一级的代码复用,有两种工具包:
- 基础工具包(内置好的),基础类由对象编程环境提供包括String、Date等
- 体系结构工具包,作为系统软件的一部分,如操作系统、数据库软件
6.4.2.2 框架复用
- 框架强调构件级的复用
- 一个框架是可定制的应用软件
6.4.2.3 模式复用
(Analysis and design patterns)
1、模式强调在开发方法过程中的复用
2、模式可以应用于开发生命周期的分析阶段、体系结构设计或详细设计阶段,因此有了分析模式、体系结构模式、设计模式
6.5 协作建模
(Collaboration modeling)
1、复合结构:相互连接的元素组合,表示运行时的实例协作,通过通信连接取得某些共同的目标
2、复合结构的建模:复合结构图
3、协作模型详细描述了为了完成期望的功能所需要的协作元素(角色)和角色之间的通信路径(连接器)
6.5.1 协作
1、协作描述相互协作的角色(元素)结构,每个元素执行特定的功能,这些元素共同完成某项期望的功能
2、协作被形象地表示为一个带有协作名字的虚椭圆,矩形图标表示协作的实体(角色),角色由连接器connector连接,在协作名字的后面可以写上分类器名。
6.5.2 复合结构
1、另一种类图的创建方式
2、将协作转换成复合结构,需要将所有的角色从椭圆中抽取出来放在类种(因此需要为角色定义任何缺失的类型),将协作连接到类上,并为连接器定义上角色名(role1、role2)
6.5.3 从用例到复合协作
复合协作由下级协作组成(实现),一个复合协作可以实一个用例,而下级协作可以表示此用例的需求
6.5.4 从协作到交互
协作可以被用于生成顺序图和通信图的手段。为此,协作角色称为了顺序图上的生命线,连接器被交互中的消息代替。
6.5.5 从交互到复合结构
复合结构图是与协作范围所对应的一种类图——输入角色(具有明确定义的类型)
协作模型不标识消息