软件工程-软件工程导论(第六版)第十一章 面向对象设计

1 引言

继续继续!面向对象设计(第十一章)

2 面向对象设计

      分析是提取和整理用户需求,并建立问题域精确模型的过程。设计则是把分析阶段得到的需求转变成符合成本和质量要求的、抽象的系统实现方案的过程。

       从面向对象分析到面向对象设计(OOD),是一个逐渐扩充模型的过程。或者说,面向对象设计就是用面向对象观点建立求解域模型的过程。

      本章首先讲述为获得优秀设计结果应该遵循的准则,然后具体讲述面向对象设计的任务和方法。

2.1 面向对象设计的准则

(1)模块化  (2)抽象 (3)信息隐藏 (4)弱耦合 (5)强内聚 (6)可重用

(1)模块化

对象就是模块
把数据结构和操作这些数据的方法

   紧密地结合在一起

(2)抽象

过程抽象
数据抽象:类

参数化抽象:C++的“模板”

(3)信息隐藏

通过对象的封装性实现

类分离了接口与实现,支持信息隐藏

(4)弱耦合

耦合:一个软件结构内不同模块之间互连的紧密程度
弱耦合:系统中某一部分的变化对其他部分的影响降到最低程度

对象之间的耦合:交互耦合&继承耦合

(5)强内聚

内聚衡量一个模块内各个元素彼此结合的紧密程度
在设计时应该力求做到高内聚
面向对象设计的3种内聚:

   服务内聚、类内聚、一般\特殊内聚

(6)可重用

尽量使用已有的类

如果确实需要创建新类,则在设计这些新类的协议时,应该考虑将来的可重复使用性

2.2 启发规则

1. 设计结果应该清晰易懂

2. 一般-特殊结构的深度应适当

3. 设计简单的类

4. 使用简单的协议

5. 使用简单的服务

6. 把设计变动减至最小

/*------------------------------------------分割线---------------------------------------------------------*/

1. 设计结果应该清晰易懂

提高软件可维护性和可重用性的重要措施
保证设计结果清晰易懂的主要因素:
  (1)用词一致 
  (2)使用已有的协议
  (3)减少消息模式的数目 

  (4)避免模糊的定义

2. 一般-特殊结构的深度应适当

类等级中包含的层次数适当

一个中等规模(大约包含100个类)的系统中,类等级层次数应保持为7±2。

3. 设计简单的类

尽量设计小而简单的类
注意以下几点:
  (1)避免包含过多的属性
  (2)有明确的定义
  (3)尽量简化对象之间的合作关系

  (4)不要提供太多服务

4. 使用简单的协议

消息中的参数不要超过3个

5. 使用简单的服务

类中的服务通常都很小

尽量避免使用复杂的服务

6. 把设计变动减至最小

理想的设计变动曲线如右图所示

在设计的早期阶段,变动较大,随着时间推移,设计方案日趋成熟,改动也越来越小了

2.3 软件重用

  2.3.1 概述

1. 重用

重用也叫再用或复用,是指同一事物不作修改或稍加改动就多次重复使用。

广义地说,软件重用可分为以下3个层次:
知识重用
方法和标准的重用
软件成分的重用

上述前两个重用层次属于知识工程研究的范畴,本节仅讨论软件成分重用问题。

2. 软件成分的重用级别

代码重用
(1)源代码剪贴
(2)源代码包含
(3)继承
设计结果重用
  重用某个软件系统的设计模型(即求解域模型)
分许结果重用
   更高级别的重用,即重用某个系统的分析模型

 3.典型的可重用软件成分

主要有以下10种:

项目计划

成本估计
体系结构
需求模型和规格说明
设计
源代码
用户文档和技术文档
用户界面
数据

测试用例

2.3.2 类构件

1. 可重用软构件应具备的特点

为使软构件也像硬件集成电路那样,能在构造各种各样的软件系统时方便地重复使用,就必须使它们满足下列要求:
(1)模块独立性强
(2)成具有高度可塑性
(3)接口清晰、简明、可靠

(4)需求模型和规格说明

2. 类构件的重用方式

实例重用
(1)使用适当的构造函数,按照需要创建类的实例
(2)用几个简单的对象作为类的成员创建出一个更复杂的类
继承重用
    (1)继承性提供了一种对已有的类构件进行裁剪的机制
多态重用
(1)使对象的对外接口更加一般化,降低了消息连接的复杂程度

(2)提供一种简便可靠的软构件组合机制

2.3.3 软件重用的效益

(1)质量(2)成本 (3)生产率

2.4 问题分解

“分而治之,各个击破”        软件工程师在设计比较复杂的应用系统时普遍采用的策略,也是首先把系统分解成若干个比较小的部分,然后再分别设计每个部分。系统的主要组成部分称为子系统

1.  子系统之间的两种交互方式

(1)客户-供应商关系
作为“客户”的子系统调用作为“供应商”的子系统,后者完成某些服务工作并返回结果。
前者必须了解后者的接口,然而后者却无须了解前者的接口,因为任何交互行为都是由前者驱动的。
(2)平等伙伴关系
每个子系统都可能调用其他子系统,每个子系统都必须了解其他子系统的接口。

子系统之间的交互更复杂,这种交互方式还可能存在通信环路。

2.  组织系统的两种方案

(1)层次组织
把软件系统组织成一个层次系统,每层是一个子系统。
上层在下层的基础上建立,下层为实现上层功能而提供必要的服务。
每一层内所包含的对象,彼此间相互独立,而处于不同层次上的对象,彼此间往往有关联。
在上、下层之间存在客户-供应商关系。低层相当于供应商,上层相当于客户。

层次结构又可进一步划分成两种模式:封闭式和开放式。

(2)块状组织
把软件系统垂直地分解成若干个相对独立的、弱耦合的子系统。

一个子系统相当于一块,每块提供一种类型的服务。

2.5 设计问题域子系统

        在面向对象设计过程中,可能对面向对象分析所得出的问题域模型做补充或修改

1. 调整需求  2. 重用已有的类 3. 把问题域类组合在一起 4. 添加一般化类以建立协议 5. 调整继承类层次 

 /*-----------------------------------------------华丽的分割线---------------------------------*/

1. 调整需求

        两种情况会导致修改通过面向对象分析所确定的系统需求

用户需求或外部环境发生了变化。

分析员对问题域理解不透彻或缺乏领域专家帮助,以致面向对象分析模型不能完整、准确地反映用户的真实需求。

2.重用已有的类

如果有可能重用已有的类,则重用已有类的典型过程如下

选择有可能被重用的已有类,标出这些候选类中对本问题无用的属性和服务,尽量重用那些能使无用的属性和服务降到最低程度的类。
在被重用的已有类和问题域类之间添加泛化关系(即从被重用的已有类派生出问题域类)。
标出问题域类中从已有类继承来的属性和服务,现在已经无须在问题域类内定义它们了。

修改与问题域类相关的关联,必要时改为与被重用的已有类相关的关联。

3. 把问题域类组合在一起

       在面向对象设计过程中,设计者往往通过引入一个根类而把问题域类组合在一起。

4. 添加一般化类以建立协议

         一些具体类需要有一个公共的协议,也就是说,它们都需要定义一组类似的服务(很可能还需要相应的属性)。在这种情况下可以引入一个附加类(例如根类),以便建立这个协议

5. 调整继承类层次

(1) 使用多重继承机制、(2) 使用单重继承机制

2.6 设计人机交互子系统

1. 分类用户 

         为了更好地了解用户的需要与爱好,以便设计出符合用户需要的界面,设计者首先应该把将来可能与系统交互的用户分类。

2. 描述用户

了解将来使用系统的每类用户的情况

用户类型。
使用系统欲达到的目的。
特征(年龄、性别、受教育程度、限制因素等)。
关键的成功因素(需求、爱好、习惯等)。
技能水平。

完成本职工作的脚本。

3. 设计命令层次

基本外观及给用户的感受都是相同的
每个程序至少有一个窗口,它由标题栏标识;
程序中大多数功能可通过菜单选用;
选中某些菜单项会弹出对话框,用户可通过它输入附加信息;
……
广大用户习以为常的许多约定
File菜单的最后一个菜单项是Exit;
在文件列表框中用鼠标单击某个表项,则相应的文件名变亮,若用鼠标双击则会打开该文件;


(2) 确定初始的命令层次

所谓命令层次,实质上是用过程抽象机制组织起来的、可供选用的服务的表示形式。

设计命令层次时,通常先从对服务的过程抽象着手,然后再进一步修改它们,以适合具体应用环境的需要。

(3) 精化命令层次

次序
整体-部分关系
宽度和深度

操作步骤

4. 设计人机交互类

人机交互类与所使用的操作系统及编程语言密切相关

       例如,在Windows环境下运行的Visual C++语言提供了MFC类库,设计人机交互类时,往往仅需从MFC类库中选出一些适用的类,然后从这些类派生出符合自己需要的类就可以了。

2.7 设计任务管理子系统

       设计工作的一项重要内容就是,确定哪些是必须同时动作的对象,哪些是相互排斥的对象。然后进一步设计任务管理子系统。

1.设计并发性

如果两个对象彼此间不存在交互,或者它们同时接受事件,则这两个对象在本质上是并发的。
通过检查各个对象的状态图及它们之间交换的事件,能够把若干个非并发的对象归并到一条控制线中。

在计算机系统中用任务(task)实现控制线,一般认为任务是进程(process)的别名。通常把多个任务的并发执行称为多任务。

划分任务,可以简化系统的设计及编码工作。这种并发行为既可以在不同的处理器上实现,也可以在单个处理器上利用多任务操作系统仿真实现。

2.设计任务管理子系统

(1) 确定事件驱动型任务

(2) 确定时钟驱动型任务

(3) 确定优先任务

(4) 确定关键任务

(5) 确定协调任务

(6) 尽量减少任务数

(7) 确定资源需求

2.8 设计数据库管理子系统

         数据管理子系统是系统存储或检索对象的基本设施,它建立在某种数据存储管理系统之上,并且隔离了数据存储管理模式(文件、关系数据库或面向对象数据库)的影响。

2.8.1 选择数据存储管理模式

1. 文件管理系统
文件管理系统是操作系统的一个组成部分,使用它长期保存数据具有成本低和简单等特点,
但是,文件操作的级别低,为提供适当的抽象级别还必须编写额外的代码。
此外,不同操作系统的文件管理系统往往有明显差异。
2. 关系数据库管理系统
3. 面向对象数据库管理系统
面向对象数据库管理系统主要有两种设计途径
扩展的关系数据库管理系统
    (1)在关系数据库的基础上,增加了抽象数据类型和继承机制。
    (2)增加了创建及管理类和对象的通用服务。
扩展的面向对象程序设计语言
      (1)扩充了面向对象程序设计语言的语法和功能,增加了在数据      库中存储和管理对象的机制。
      (2)开发人员可以用统一的面向对象观点进行设计,不再需要区分存储数据结构和程序数据结构(即生命期短暂的数据)。

2.9 设计类中的服务

        面向对象分析得出的对象模型,通常并不详细描述类中的服务。面向对象设计则是扩充、完善和细化面向对象分析模型的过程,设计类中的服务是它的一项重要工作内容。

2.9.1  确定类中应有的服务

         确定类中应有的服务需要综合考虑对象模型、动态模型和功能模型,才能正确确定类中应有的服务。对象模型是进行对象设计的基本框架。
一张状态图描绘了一类对象的生命周期,图中的状态转换是执行对象服务的结果。
功能模型指明了系统必须提供的服务。
状态图中状态转换所触发的动作,在功能模型中有时可能扩展成一张数据流图。
数据流图中的某些处理可能与对象提供的服务相对应,有一些规则有助于确定操作的目标对象(即应该在该对象所属的类中定义这个服务)。
当一个处理涉及多个对象时,通常在起主要作用的对象类中定义这个服务。

2.9.2 设计实现服务的方法

1. 设计实现服务的算法
应该考虑下列几个因素:
(1)算法复杂度。
         通常选用复杂度较低(即效率较高)的算法,但也不要过分追求高效率,应以能满足用户需求为准。
(2) 容易理解与容易实现。
         容易理解与容易实现的要求往往与高效率有矛盾,设计者应该对这两个因素适当折衷。
(3) 易修改。
         应该尽可能预测将来可能做的修改,并在设计时预先做些准备。
2. 选择数据结构
        在分析阶段,仅需考虑系统中需要的信息的逻辑结构,在面向对象设计过程中,则需要选择能够方便、有效地实现算法的物理数据结构。
3. 算法与数据结构的关系
         设计阶段是解决“怎么做”的时候了,因此,确定实现服务方法中所需要的算法与数据结构是非常关键的。主要考虑下列因素:
(1) 分析问题寻找数据特点,提炼出所有可行有效的算法;
(2) 定义与所提炼算法相关联的数据结构;
(3) 依据此数据结构进行算法的详细设计;
(4) 进行一定规模的实验与评测;
(5) 确定最佳设计。

4. 定义内部类和内部操作

        在面向对象设计过程中,可能需要增添一些在需求陈述中没有提到的类,这些新增加的类,主要用来存放在执行算法过程中所得出的某些中间结果。

        此外,复杂操作往往可以用简单对象上的更低层操作来定义。因此,在分解高层操作时常常引入新的低层操作。在面向对象设计过程中应该定义这些新增加的低层操作。

2.10 设计关联

1.  关联的遍历

在应用系统中,使用关联有两种可能的方式:单向遍历和双向遍历。
在使用原型法开发软件的时候,原型中所有关联都应该是双向的,以便于增加新的行为,快速地扩充和修改原型。

2.  实现单向关联

        用指针可以方便地实现单向关联。如果关联的重数是一元的(如图a所示),则实现关联的指针是一个简单指针;如果重数是多元的,则需要用一个指针集合实现关联(参见图b)。

3.  实现双向关联

(1)只用属性实现一个方向的关联

(2)两个方向的关联都用属性实现

(3)用独立的关联对象实现双向关联

4.  关联对象的实现

         9.4.2节曾经讲过,可以引入一个关联类来保存描述关联性质的信息,关联中的每个连接对应着关联类的一个对象。

实现关联对象的方法取决于关联的重数。

对于一对一关联来说,关联对象可以与参与关联的任一个对象合并。
对于一对多关联来说,关联对象可以与“多”端对象合并
如果是多对多关联,则关联链的性质不可能只与一个参与关联的对象有关,通常用一个独立的关联类来保存描述关联性质的信息,这个类的每个实例表示一条具体的关联链及该链的属性。

2.11 设计优化

设计的优化可以从以下的几个方面考虑。

(1)确定优先级 (2)提高效率 (3)调整继承关系

3 结束语

今天到此为止!改天再见!

猜你喜欢

转载自blog.csdn.net/chen_yongbo/article/details/80573274