1
 软件工程
1.9.2.1 7.2.1 面向对象的设计准则

7.2.1 面向对象的设计准则

系统设计的优劣直接影响软件的质量。在设计阶段一定要遵循设计准则,按照设计策略设计出优秀的方案。下面按照指导传统设计方法的基本设计原理,给出面向对象设计对应的准则和策略。

1.模块化

对于复杂的系统而言,降低复杂性的方法是将系统模块化,也就是将一个复杂的大系统分解为若干个相对简单的较小部分,称为子系统(Subsystem)。如果一个子系统仍然是复杂的,那么继续分解子系统直到其易于开发和管理为止。这种“分而治之,各个击破”的策略来自于人们处理复杂事物的经验,也同样适用于软件开发过程。

子系统是一个定义明确的软件组件,它向其他子系统提供服务。一个服务是一组有着共同目标的相关操作,这些提供给其他子系统的操作形成了子系统的接口。子系统接口只对外部提供操作的名称、参数、类型和返回值等,而对操作的实现进行了封装,因此,在接口不变的情况下,子系统内部实现的修改对外部调用影响很小,从而增加了系统的可维护性。图7-10给出了一个系统进行层次分解的例子。其中,子系统可以划分为不同的层次,每一层使用底层所提供的服务,并为高层提供服务。需要注意的是,子系统的层数最好在3~7层之间,同一层子系统的数量为5~9个比较合适。

系统分解的另一种形式是将系统分解为对等的子系统,每一个子系统负责不同类型的服务,子系统之间相互独立。

img127

图7-10 系统层次分解示意图

2.抽象

面向对象方法不仅支持过程抽象,而且支持数据抽象。类实际上是一种抽象的数据模型,它对外开放的公共接口构成了类的规格说明(协议),这种接口规定了外界可以使用的合法操作符,利用这些操作符可以对类实例中包含的数据进行操作。使用者无须知道这些操作符的实现算法和类中数据元素的具体表示方法,就可以通过这些操作符使用类中定义的数据。通常把这类抽象称为规格说明抽象。

此外,某些面向对象的程序设计语言还支持参数化抽象。所谓参数化抽象,是指当描述类的规格说明时,并不具体指定所要操作的数据类型,而是把数据类型作为参数。这使得类的抽象程度更高,应用范围更广,可重用性更高。例如,C++语言提供的“模板”机制就是一种参数化的抽象机制。

3.耦合性

耦合性表示两个子系统之间的关联程度。如果一个子系统发生变化对另一个子系统的影响很小,则称它们是松散耦合的;如果变化的影响很大,则称它们是紧密耦合的。显然,耦合越松散越好。

松散耦合是优秀设计的一个重要标准,因为这有助于使系统中某一部分的变化对其他部分的影响降到最低程度。在理想情况下,对某一部分的理解、测试或修改无须涉及系统的其他部分。

一般说来,对象之间的耦合可分为以下两大类。

1)交互耦合

如果对象之间的耦合通过消息连接来实现,则这种耦合就是交互耦合。为使交互耦合尽可能松散,应该遵守下述准则:尽量降低消息连接的复杂程度。应该尽量减少消息中包含的参数个数,降低参数的复杂程度,减少对象发送(或接收)的消息数。

2)继承耦合

与交互耦合相反,设计中应该提高继承耦合程度。继承是一般化类与特殊类之间耦合的一种形式。从本质上看,通过继承关系结合起来的基类和派生类,构成了系统中粒度更大的模块。因此,它们彼此之间应该结合得越紧密越好。

为获得紧密的继承耦合,特殊类应该确实是对它的一般化类的一种具体化。因此,如果一个派生类摒弃了其基类的许多属性,则它们之间是松耦合的。在设计时应该使特殊类尽量多继承并使用其一般化类的属性和服务,从而更紧密地耦合到其一般化类。

4.内聚性

所谓内聚,是一个模块内各个元素彼此结合的紧密程度。结合得越紧密内聚越强,结合得越松散内聚越弱。强内聚也是衡量设计优良的一个重要标准。在面向对象设计中,内聚可分为下述三类。

1)服务内聚

一个服务应该是单一的,即只完成一个任务。

2)类内聚

类内聚要求类的属性和服务应该是高内聚的,而且它们应该是系统任务所必需的。一个类应该只有一个功能,如果某个类有多个功能,通常应该把它分解成多个专业的类。

3)一般-特殊内聚

一般-特殊内聚表示一般-特殊结构符合领域知识的表示形式,也就是说,特殊类应该尽量地继承一般类的属性和服务。这样的一般-特殊结构是高内聚的。

例如,虽然表面看起来飞机与汽车有相似的地方(都用发动机驱动,都有轮子,……),但是,如果把汽车和飞机都作为“机动车”类的子类,则不符合领域知识的表示形式,这样的一般-特殊结构是低内聚的。高内聚的一般-特殊结构应该是,设置一个抽象类“交通工具”,把飞机和机动车作为交通工具类的子类,而汽车又是机动车类的子类。

5.复用性

对于建立软件系统而言,所谓复用就是利用某些已开发的、对建立目标系统有用的软件元素来生成新的软件系统。在一个新系统中,大部分的内容是成熟的,只有小部分内容是新的。一般地,可以相信成熟的东西总是比较可靠的,而大量成熟的工作可以通过复用快速实现,人们应该把大部分精力用于小比例的创新工作中,而把小部分精力用于大比例的成熟工作中,这样才能把系统完成得又快又好。复用有两方面的含义:一是尽量使用已有的类(包括开发环境提供的类库,及以往开发类似系统时创建的类),二是如果确实需要创建新类,则在设计这些新类的协议时,应该考虑将来的可重复使用性。

将具有一定集成度并可以重复使用的软件组成的单元称为软件构件。软件复用是直接使用已有的软件构件,通过组装或合理的修改生成新系统。一方面,软件复用方法合理化并简化了软件开发过程,减少了总的开发工作量与维护代价,既降低了软件的成本又提高了生产效率;另一方面,由于软件构件是经过反复使用验证的,自身具有较高的质量,因此,由软件构件组成的新系统也具有较高的质量。除此之外,设计模式也是一种复用,它通过为对象协作提供思想和范例来强调方法的复用,这些思想都是复用性的体现。