设计模式中类的关系

1、泛化(Generalization)

 

什么是泛化关系?用一个例子简单的说:假设ABC的父类,BC具有公共类(父类)A,说明ABC的一般化(概括,也称泛化),B、C是A的特殊化。

  在编程上,泛化关系(Generalization)就是我们常说的继承关系,称为“is-a-kind-of”关系,泛化关系用于描述父类与子类之间的关系,父类又称作基类或超类,子类又称作派生类。在UML中,泛化关系用带空心三角形的直线来表示。

  在代码实现时,使用面向对象的继承机制来实现泛化关系,如在Java语言中使用extends关键字、在C++/C#/OC中使用冒号“:”来实现。

  UML示例图如下所示:

UML当中,对泛化关系有三个要求:

  • 子类与父类应该完全一致,父类所具有的属性、操作,子类应该都有;
  • 子类中除了与父类一致的信息以外,还包括额外的信息;
  • 可以使用父类的实例的地方,也可以使用子类的实例。

2、实现(Realization)

 

       实现关系是用来描述接口和实现接口的类或者构建结构之间的关系,接口是操作的集合,而这些操作就用于规定类或者构建结构的一种服务。

  在接口和类之间的实现关系中,类实现了接口,类中的操作实现了接口中所声明的操作。在UML中,类与接口之间的实现关系用带空心三角形的虚线来表示。UML示例图如下所示:

    依赖关系是一种使用关系,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系。可以简单的理解,就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是B类的变化会影响到A;比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖;表现在代码层面,为类A在某个方法中使用类B是作为类A的方法参数、方法中的局部变量、或者静态方法调用。

      在UML中,依赖关系用带箭头的虚线表示,由依赖的一方指向被依赖的一方。UML示例图如下所示:

 

示例代码如下(People.m)

#import "People.h"

@implementation People

- (void)eat:(Food *)food {
    NSLog(@"I am eating food.");
}

- (void)read:(Book *)book {
    NSLog(@"I am reading.");
}

@end

4、关系(Association)
    关联关系是类与类之间最常用的一种关系,它是一种结构化关系,用于表示一类对象与另一类对象之间有联系。它体现的是两个类、或者类与接口之间语义级别的一种强依赖关系,比如我和我的朋友。这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的,关联可以是单向、双向的。表现在代码层面,为被关联类B以类属性的形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量。

      在UML类图中,用实线连接有关联的对象所对应的类,在使用JavaC#C++等编程语言实现关联关系时,通常将一个类的对象作为另一个类的属性。

      1.双向关联。

      默认情况下,关联是双向的,双向的关联可以有两个箭头或者没有箭头。

      2.单向关联。

      类的关联关系也可以是单向的,单向关联用带箭头的实线表示。

      单向关联和双向关联的UML示例图如下所示::

 

【说明】:上图中,TeacherStudent是双向关联,Teacher有多名StudentStudent也可能有多名Teacher(两个类连线下面的*表示多对多的关系)。但StudentCourse间的关系为单向关联,一名Student可能有多门Course,课程是个抽象的东西,因此不拥有Course单向关联和双向关联的示例代码如下(TeacherStudent类的定义)

Teacher.h文件:

@class Student;
@interface Teacher : NSObject {
    Student     *_student;
}

@property (nonatomic, retain) Student *student;

@end

Student.h文件:
#import "Course.h"
@class Teacher;
@interface Student : NSObject{
    Teacher     *_teacher;
    Course      *_course;
}

@property (nonatomic, retain) Teacher *teacher;
@property (nonatomic, retain) Course *course;

@end

【注意】:可能大家注意到了,在Student类的定义中,包含Course类用的是:#import "Course.h";而包含Teacher类用的是:@class Teacher;。这是因为Teacher类和Student是双向关联,如果直接用import,那么在编译的时候,编译器会报错,至于@class#import的区别,在后面会进行介绍。

      3.自关联。

      在系统中可能会存在一些类的属性对象类型为该类本身,这种特殊的关联关系称为自关联。比如我们在数据结构中描述树结构,会建一个节点Node类,Node类有一个指针父节点也是Node类型。

5、聚合(Aggregation)

     聚合关系是关联关系的一种特例,它体现的是整体与部分的关系,即has-a的关系,此时整体与部分之间是可分离的,它们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。比如计算机与CPU、公司与员工的关系等。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。

      在聚合关系中,成员类是整体类的一部分,即成员对象是整体对象的一部分,但是成员对象可以脱离整体对象独立存在。在UML中,聚合关系用带空心菱形的直线表示。UML示例图如下所示:

示例代码如下(概要,完整源码见附件)Computer.m文件

#import "Computer.h"

@implementation Computer
@synthesize centralProcessingUnit = _centralProcessingUnit;

- (id)initWithCpu:(CentralProcessingUnit *)cpu{
    self = [super init];
    
    if (self != nil){
        self.centralProcessingUnit = cpu;
    }
    return self;
}

- (void)dealloc{
    [_centralProcessingUnit release];
    NSLog(@"Computer dealloc");
    [super dealloc];
}

@end
调用代码:
CentralProcessingUnit *centralProcessingUnit = [[CentralProcessingUnit alloc] init];
Computer *computer = [[Computer alloc] initWithCpu:centralProcessingUnit];

// computer生命周期结束
[computer release];

// centralProcessingUnit还可以进行其他操作.....
[centralProcessingUnit release];
     从调用代码我们可以看到,我们创建了一个独立的centralProcessingUnit对象,然后将这个对象传入了Computerinit函数。当computer对象生命周期结束的时候,centralProcessingUnit对象如果还有其他指向它的引用,是可以继续存在的。也就是说,它们的生命周期是相对独立的。

6、组合(Composition)

    组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合;它同样体现整体与部分间的关系,但此时整体与部分是不可分的,它们具有统一的生存期,整体的生命周期结束也就意味着部分的生命周期结束,部分对象与整体对象之间具有同生共死的关系,组合关系中的部分,是不能在整体之间进行共享的。比如人和眼睛,当然,有人会说现在医学发达,眼睛可以移植给别人,如果是这样的话,你可以理解人和眼睛的关系为聚合,这都是在具体的场景下来确定的。表现在代码层面,和关联关系是一致的,只能从语义级别来区分。在组合关系中,成员类是整体类的一部分,而且整体类可以控制成员类的生命周期,即成员类的存在依赖于整体类。  在UML中,组合关系用带实心菱形的直线表示。UML示例图如下所示:

示例代码如下(完整代码见附件)

People.m文件:

#import "People.h"

@implementation People
@synthesize eye = _eye;

- (id)init{
    self = [super init];
    if (self != nil){
        _eye = [[Eye alloc] init];
    }
    return self;
}

- (void)dealloc{
    [_eye release];
    NSLog(@"People dealloc");
    [super dealloc];
}

@end
       从上面我们可以看到,Eye对象是在People对象里面创建的,所以在People对象生命周期结束的时候,Eye对象的生命周期也同样结束了。
      总结下面这张UML图(该图为网上找到的),比较形象地展示了各种类图关系:

      对于继承、实现这两种关系没多少疑问,它们体现的是一种类与类、或者类与接口间的纵向关系;其他的四者关系则体现的是类与类、或者类与接口间的引用、横向关系,是比较难区分的,有很多事物间的关系要想准确定位是很难的,前面也提到,这几种关系都是语义级别的,所以从代码层面并不能完全区分各种关系;但总的来说,后几种关系所表现的强弱程度依次为:组合>聚合>关联>依赖。








posted @ 2014-05-12 20:34  Forrest.Wang  阅读(163)  评论(0编辑  收藏  举报