(第三部分:面向对象分析与设计方法,第8章)
Created: 2022-05-19 Thu 00:05
第三部分:面向对象分析与设计方法
第8章:面向对象设计
面向对象设计的主要任务是在 面向对象分析 的基础上完成:
由于在类中封装了 属性 和 方法 ,因此在类设计中已经包含了传统方法中的 过程设计 。
Figure 1: 系统环境图
每个外部实体都通过 某一接口(带阴影的小矩形) 与目标系统进行通信。
根据客户的需求选择 体系结构 风格,之后对可选的体系结构 风格 或 模式 进行分析。 以导出最适合 客户需求 和 质量属性 的结构。
体系结构设计 往往要经过多次 反复迭代 和 求精 才能得到满意的结果。
对于 面向对象的系统 ,典型的子系统有:
对象设计 以问题领域的对象设计为核心,其结果是一个详细的 对象模型 。
对象设计过程包括:
体系结构设计 描述了建立计算机系统所需的 数据结构 和 程序构件 。 一个好的体系结构设计要求:
在 面向对象 的程序设计中, 类 和 接口 是程序的基本组成单元。
一个典型程序需要:
在计算机程序中,要设计和实现所有类都具有唯一的名字, 在 不同的阶段 或从 不同的角度 可以将它们称为 设计类 、 实现类 、 系统类 或 应用类 等。
交互依赖性 也称为 方法依赖性 ,是通过消息连接产生的。 类之间的依赖性相当于类成员之间的依赖性。
Figure 2: 交互依赖性
public class CActioner {
EEmployee emp;
public void do1() {
emp.do3();
}
}
public class EOutMessage {
EEmployee emp;
public void do2() {
emp.do3();
}
}
接口 是不可直接实例化的特性集合的声明,即其对象不能直接实例化, 需要通过类来实现,实现接口的类需要实现接口中声明的方法。
接口 与 抽象类 有相似之处,抽象类是 至少包含一个没有实现的方法的类 。 如果在一个抽象类中 所有的方法都没有实现 ,则称其为 纯抽象类 。
Class1
实现了 Interface1
和 Interface2
,而 Class2
只实现了 Interface2
。
Figure 3: 实现依赖性
Figure 4: 使用依赖性
public class Class1 {
Interface1 myInterface;
public void do1() {
myInterface.op1();
}
}
Class1
使用 Interface1
, Interface1
使用 Interface2
。
在Java语言中,
当系统中涉及的类的数量比较多时,往往会将关系紧密的类组织到 包(package) 中。 按照UML的定义, 包是一组命名的建模元素集合。一个包可能包含其他包。
在UML中,包是一个 逻辑设计 概念。最终,包必须 实现 并 映射 为编程语言。 Java和C#语言提供了包 概念 到 实现 的直接映射。 通过类的名字空间和引入其他包的形式来支持实现包。
如果包A的一些成员在某种程度上引用了包B的某些成员(包A导入了包B的一些成员), 这隐含着双重含义:
本质上,两个 包之间的依赖性 来自于两个包中 类之间的依赖性 。 类之间的 循环依赖性 是个特别棘手的问题。
Figure 5: 包之间的循环依赖性
Figure 6: 消除包之间的循环依赖性
传统的构件也称为模块,是软件体系结构的一部分,它承担如下3个重要角色之一:
在面向对象的软件工程环境中,面向对象技术已达到了类级复用,这样的复用粒度还太小。 构件级复用 则是比 类级复用 更高一级的复用,它是对一组类的组合进行封装, 并代表完成一个或多个功能的 特定服务 ,也为用户提供了多个 接口 。 整个构件隐藏了具体的实现,只用接口对外提供服务。
为了能够支持复用,软件构件应具有以下特性:
在目前的很多系统中,构件被实现为更大粒度的单元,系统中的构件只能有一个实例。 例如,一个数据库服务器可以作为构件, 而它所管理的数据库(可以是一个,也可以是多个)并不是构件, 而是数据库“对象”实例。
在1996年的面向对象程序设计欧洲会议上,面向构件程序设计组提出以下定义:
软件构件 是一种 组装单元 ,它具有规范的 接口规格说明 和显式的 语境依赖 。 软件构件可以被 独立部署 ,并由第三方 任意组装 。
OMG UML规范中将构件定义为:
系统中某一 定型化的 、 可配置的 和 可替换的 部件 , 该 部件 封装了 实现 并暴露一系列 接口 。
上面的两个定义中都提到接口的概念,构件之间是通过接口相互连接的。 接口是可被客户访问的具体操作的集合,每个操作有规定的语义。
构件图 表示构件之间的 依赖关系 , 每个构件 实现(支持) 一些接口,并 使用 另一些接口。
Figure 7: 构件之间的依赖关系
在大型和复杂的软件系统中,首先根据需求的功能模型(用例模型), 将 系统 分解成若干个 部分 ,每一部分又可分解为若干个 子系统 或 类 , 每个 子系统 还可以由 更小的子系统 或 类 组成。
Figure 8: 系统结构的类图
服务 是一组有 公共目的 的相关操作。 而子系统则通过 给其他子系统提供服务 来发挥自己的能力。 供其他子系统调用的某个子系统的 操作集合 就是 子系统接口 。
子系统的接口包括:
面向对象的系统设计 主要关注 每个子系统提供服务的定义 , 即枚举所有的 操作 、 操作参数 和 行为 。 因此,当编写子系统接口的文档时,应 不涉及子系统实现的细节 , 其目的是 减少子系统之间的依赖性 。
子系统分层 的目的是 建立系统的层次结构 。
在一个系统的层次结构中,如果:
Figure 9: 封闭体系结构示例
Figure 10: 开放体系结构示例
Coad & Yourdon 基于 MVC(Model-View-Controller)模型 , 在 逻辑上 将系统划分为4个部分,每一部分又可分为若干个 子系统 。
Coad & Yourdon 在设计阶段中继续采用了 分析阶段 中提到的 5个层次 , 用于建立系统的4个组成部分。
Figure 11: 典型的面向对象设计模型
在软件系统中,提供服务的一端称为 服务器端 ,而将使用服务的一端称为 客户端 。
子系统之间的交互方式 有两种:
总的来说, 单向交互 比 双向交互 更容易理解,也更容易设计和修改, 因此,应该尽量使用 客户-供应商关系 。
把子系统组织成完整的系统时,有 水平层次组织 和 垂直块状组织 两种方案可供选择。
混合使用 层次结构 和 块状结构 ,可以成功地由多个子系统组成一个完整的软件系统。 当混合使用层次结构和块状结构时, 同一层次可以由若干块组成 ,而 同一块也可以分为若干层 。
典型的面向对象系统一般由三层组成,即:
那么,首先从哪一层开始设计呢?
参与人数 | 0 |
---|---|
数据库层 | 0 |
业务逻辑层 | 0 |
用户界面层 | 0 |
面向对象的分析模型 包括有 用例图 、 类图 、 顺序图 和 包图 , 主要是对问题领域进行描述,基本上不考虑 技术实现 ,也不考虑 数据库层 和 用户界面层 。 面向对象分析 所得到的 问题域模型 可以直接应用于系统的 问题域部分的设计 。
在面向对象设计过程中,可能对 面向对象分析 所得出的 问题域模型 做以下方面的补充或调整。
用户界面(即人机交互界面) 是人机交互的主要方式, 用户界面的质量直接影响到用户对软件的使用, 对用户的 情绪 和 工作效率 也会产生重要影响, 也直接影响用户对软件产品的评价,从而影响软件产品的 竞争力 和 寿命 。
在设计阶段必须根据需求把 交互细节 加入到用户界面设计中, 包括人机交互所必需的 实际显示 和 输入 。
在某些情况下,界面设计师可以从每个界面 状态草图 开始, 如在 各种环境下用户界面 看起来是个什么样子; 接下来定义 对象 、 动作 和 其他重要的设计信息 。
在Web设计的定期专栏中,Jean Kaiser提出了下面的 设计目标 , 无论 应用的领域 、 规模 和 复杂度 如何,这些目标实际上可以适用于任何Web应用系统。
下面的工作代表了Web应用系统界面设计的基本工作流程:
任务 是 进程 的别称,是执行一系列活动的一段程序。 当系统中有许多并发行为时,需要依照各个行为的协调和通信关系, 划分各种任务,以简化并发行为的设计和编码。
任务管理 主要包括任务的 选择 和 调整 。
常见的任务有:
设计任务管理子系统时,需要确定各类任务,并将任务分配给适当的硬件或软件去执行。
在传统的 结构化设计 方法中,很容易将 实体-关系图 映射到 关系数据库 中。 而在 面向对象设计 中,我们可以将 UML类图 看作 数据库 的 概念模型 , 但在UML类图中除了类之间的 关联关系 外,还有 继承关系 。
在映射时可以按下面的规则进行映射:
对象设计 以 问题域的对象设计 为核心,其结果是一个 详细的对象模型 。 经过多次反复的 分析 和 概要设计 之后,设计者通常会发现有些内容没有考虑到。 这些没有考虑到的内容,会在 对象设计 的过程中被发现。
对象设计过程 包括:
在面向对象设计过程中, 设计模式 是开发者通过很长时间的实践而得到的重复出现问题的 模板化解决方案 。 设计模式包括4个要素:
接口规格说明 包括以下活动:
典型的重构活动的例子包括:
几种常用的优化方法: