浅议面向对象关系的度量

日期:2014-07-31点击次数:9330

       记得当初学习面向对象设计/编程(OOD/OOP)的时候,时常对对象间的几种关系搞不清楚,查找了各种资料,却发现这些资料存在模糊与矛盾的地方,越发糊涂。于是索性根据这些资料和我的理解逐步硬性的整理出一套设计与编码过程中的规律,便于记忆与遵循。现分享之,错误之处,敬请斧正。
一、 关系的分类
       根据自然界中真实存在的各种事物,人们归纳出了各种事物之间的相互关系。软件业中继续借用这种关系。这些对象的关系主要有以下几种:

1. 依赖(Dependency)

       UML图表示:箭头方向表示需要依赖的对象。
       万事万物都存在联系,而这些联系,正是存在于供给关系中,也就是一个事物依赖于另一个事物。如:植物依赖阳光、依赖雨露,动物依赖食物、依赖于水,汽车依赖于轮子、发动机,学生学习依赖于课本,士兵战斗依赖于钢枪。等等…
在软件的设计与开发中,大到各种系统、子系统,再到程序、组件、模块,小到类、对象、函数,都存在这种广义上的依赖关系。
只是依赖的方式、强度有所不同,以下的各个小节中各种关系都可以看出是广义依赖的特例。通常会单独将这些关系单独命名,以示与侠义依赖的区别。
狭义依赖关系,就是要完成具体某件事情,缺少某些条件不行,而又不是泛化、拥有的关系。即这个依赖的对象是必要条件。体现在方法参数的调用中,是一种use a的关系。

2. 关联(Association)

       顾名思义,关联就是两个对象间有关系。表示一个对象知道另一个对象。
       UML图表示:可以看出这个图和依赖的图形很相似,是的,关联就是一种特殊的广义依赖关系。只是这种“依赖”关系比狭义依赖关系更强,所以表现为实线。不单单需要知道对方是谁(通常是抽象的类对象),还需要知道对方的属性、方法(需要内部创建实体类对象)等特点,是has a的关系。
       图上的文字,表示两个对象之间的数量关系:*表示多个,1表示一个。0…*表示0个到多个不等,是一种范围表示。0…1表示0个到1个的范围。
       比较常用是四种数量关系:1对1,1对多,多对1,多对多。
       普通的关联关系是一种很弱的关系。通常体现在全局变量、局部变量或静态方法的调用中。
       比如:一个人与报纸的关系,人可以看报纸,但没有报纸对人的定义不构成影响。
       关联关系有双向关联和单向关联。双向关联:两个类都知道另一个类的公共属性和操作。单向关联:只有一个类知道另外一个类的公共属性和操作。大        多数关联应该是单向的,单向关系更容易建立和维护,有助于寻找可复用的类。

3. 聚合(Aggregation)

       顾名思义,“聚合”就是聚在一起合作的意思。
       UML图表示: 可以看出这个图和关联的图形很相似,聚合就是一种特殊的关联关系。只是这种关联关系比普通的关联关系更强,因为有了“部分和整体”的关系,但仍然比较弱,所以采用空心的菱形来表示这种弱的关系。这种“部分和整体”的关系是较为松散的,部分和整体的生命周期是不一样的。整体不存在了,不意味着个体部分的消亡,是一种物理形式的管理关系。
       具体表现为,如果A由B聚合成,表现为B是A的成员对象,但是B对象可以不在A创建(构造、初始化)的时刻创建,并且B可以不在A销毁(析构)的时刻销毁。通常采用外部创建,再通过方法传递指针(或引用)给类的成员。
       比如:雁群,到达目的地后,雁群散了,不存在了,但大雁本身的个体还是存在的,待来年,又会组成新的雁群。

4. 合成(Composition)

       也有翻译为“组合”的,但中文与“聚合”的字面意思易发生混淆,我认为翻译成“合成”更为合适。为什么呢?
       因为合成关系是是一种强的“部分和整体”关系,而且生命周期是一样的。也就是说部分与整体同生共死,是化学形式的关联关系,所以叫“合成”更为妥当。
       具体表现为,如果A由B合成,表现为B是A的成员对象, B对象必须在A创建(构造、初始化)的时刻创建,并且B在A销毁(析构)的时刻销毁。
       比如:大雁,身体的每一个部分与整体就是合成关系,大雁死掉了,翅膀、身体、头颅也将死掉。
       UML图表示: 可以看出这个图和关联、聚合的图形很相似,只是实心的菱形就表现出了合成要强于聚合关系,是最强的关联关系。
       合成新的对象完全支配其组成部分,包括它们的创建和湮灭等。一个合成关系的成分对象是不能与另一个合成关系共享的,而聚合可以。
       换成是值的聚合(Aggregation by Value),而一般说的聚合是引用的聚合(Aggregation by Reference)。

5. 继承(Generalization)

       UML图表示: 可以看出这个图和依赖的图形很相似,继承也是一种特殊的依赖关系。只是这种依赖关系更强,三角的箭头,恰恰表示        这种强的方向上的依赖关系。
       继承与我们日常生活中的继承是一个意思。是一种Is a的关系。
       继承是一种最强的依赖关系,子类离不开父类,没有父类,子类什么都不是。与普通的依赖不同的是,子类一瞬间也离不开目标类,在其出生、甚至定义那刻起,就已注定是父类的延续。
       自然界对各种物种的划分,正是这种继承关系的经典体现:门、纲、目、科、属、种。一层一层的继承,下一层比上一层多了一些更多的属性、行为,而又属于上一层。
       有一种特殊的继承,就是接口,是最为抽象的类。这种接口类,通常用作对象的定义来传递信息,所以叫做接口。在C++里就是纯虚类的形式,这种接口类,只定义了方法(即:成员函数),而没有进行实现。具体的实现要靠子类来完成。这样同样的定义,通过不同子类的实现形式,就能达到运行时“多态”的效果。有接口类的形式,就可以达到设计模式中常说的“依赖倒转原则”所要求的效果。
       因为是最强的依赖关系,所以,耦合度极强。这是我们不希望看到的,如何解决这个问题呢?请参读“合成复用原则”的相关资料。
二、 关系的度量
       因为以上各种关系,都存在着或多或少的相同点,在实际的设计或开发过程中,很难界定该采取什么样的关系。这就要求设计人员需要经验的积累,根据需要灵活的将各种关系进行转换,掌握好这个度。根据他们的强度顺序:依赖->关联->聚合->合成->继承。尽量采用低强度的关系,在低一级的关系不能满足的情况下,逐渐升级。
       在实际的编码过程中,与这些关系是怎样的对应的呢?
       继承关系非常明了,其他四种关系通过我个人理解,进行了较为规范的定义,将通常的对应关系姑且加以说明,注意,只是通常。请看以下矩阵:
   

 

 

内部构造、释放

外部构造、释放

成员对象(部分整体关系)

合成

聚合

(全局/静态)局部对象/参数

关联

依赖

 
 

       合成:类内定义成员对象指针或者成员对象,在类的构造、初始化过程中进行对象创建,完成对成员对象的调用后,在内部进行对象释放。
       聚合:类内定义成员对象指针,在外部创建对象,并通过特定赋值函数的参数将外部对象的指针赋值给成员对象指针(或一个引用的过程)。完成对成员 对象的调用后,在类的外部进行对象释放。
       关联:类内方法中创建局部对象,使用完成后在内部进行释放。
       依赖:在类的外部创建对象,在某个类成员函数使用时,通过参数将外部对象传入到成员函数内使用。完成对成员对象的调用后,在类的外部进行对象的释放。

 

软件部   钟生晖


姓名:
性别:
电话:
E-mail
问题:
问题描述: