UML中类之间的五种关系及其在C++代码中的表现形式

借鉴资料:博客

类与类之间的关系

类与类之间的关系可以根据关系的强度依次分为以下五种:

依赖关系(Dependency)---关联关系(Association)---聚合(Aggregation)---组合(Composition)---泛化(Generalization)

1.依赖关系(Dependency)

依赖关系使用虚线加箭头表示,如下图所示:

 UML中类之间的五种关系及其在C++代码中的表现形式

 这个例子可能不太好(Animal体内有Water,),换一个:

UML中类之间的五种关系及其在C++代码中的表现形式

解释以下:Person 和 Computer之间是没有关系的,但是由于偶尔的需要,Person需要使用Computer,这时Person就依赖于Computer.

依赖关系是五种关系中耦合最小的一种关系。

类A要完成某个功能必须引用类B,则类A依赖类B。C#不建议双向依赖,也就是相互引用。

上述依赖关系在代码中的表现形式:这两个关系类都不会增加属性。

class Person
{
public:
    Person()
    {

    }
};
class Computer
{
public:
    Computer()
    {

    }
};

那么,Person类如何使用Computer类呢?有三种方式:

依赖关系的三种表现形式:

1.Computer类是public的,Person类可以调用它。

2.Computer类是Person类中某个方法的局部变量,则Person类可以调用它。

3.Computer类作为Person类中某个方法的参数或返回值。

代码如下:

//2.Computer类是Person类中某个方法的局部变量,则Person类可以调用它
#include<bits/stdc++.h>
using namespace std;
class Computer{
public:
    Computer(){
    }
};

class Person{
public:
    Person(){
    }
    void Programing(){
        Computer *computer = new Computer();
    }
};

int main(){

}

【解释】

Person有一个Programing方法,Computer类作为该方法的变量来使用。

注意Computer类的生命周期,当Programing方法被调用的时候,才被实例化。

持有Computer类的是Person类的一个方法,而不是Person类,这点是最重要的。

//3.Computer类作为Person类中某个方法的参数或返回值。
#include<bits/stdc++.h>
using namespace std;
class Computer{
public:
    Computer(){
    }
};

class Person{
public:
    Person(){
    }
    Computer *Programing(Computer *it){
        return it;
    }
};

int main(){

}

【解释】

Computer类被Person类的一个方法所持有,生命周期随着方法执行结束而结束。

在依赖关系中,必须使用这三种方法之一。

2.关联关系(Association)

 关联关系是实线加箭头表示。表示类之间的关系比依赖要强。

例如,水和气候是关联的,表示如下:

UML中类之间的五种关系及其在C++代码中的表现形式

在代码中的表现如下:

 

//关联 在Water类属性中增加了Climate类。
#include<bits/stdc++.h>
using namespace std;
class Climate{
public:
    Climate(){
    }
};

class Water{
public:
    Climate *climate = new Climate();
    Water(){
    }

};

int main(){

}

可见,在Water类属性中增加了Climate类。

关联关系有单向关联、双向关联、自身关联、多维关联等等。其中后三个可以不加箭头。

单向关联:

UML中类之间的五种关系及其在C++代码中的表现形式

双向关联:

UML中类之间的五种关系及其在C++代码中的表现形式

自身关联:

UML中类之间的五种关系及其在C++代码中的表现形式

多维关联:

UML中类之间的五种关系及其在C++代码中的表现形式

关联和依赖的区别:

  • 从类的属性是否增加的角度看:

发生依赖关系的两个类都不会增加属性。其中的一个类作为另一个类的方法的参数或者返回值,或者是某个方法的变量而已。

发生关联关系的两个类,其中的一个类成为另一个类的属性,而属性是一种更为紧密的耦合,更为长久的持有关系。

  • 从关系的生命周期来看:

依赖关系是仅当类的方法被调用时而产生,伴随着方法的结束而结束了。

关联关系是当类实例化的时候即产生,当类销毁的时候,关系结束。相比依赖讲,关联关系的生存期更长。

3.聚合(Aggregation)

 4.组合(Composition)

引用程杰的《大话设计模式》里举大那个大雁的例子 :

大雁喜欢热闹害怕孤独,所以它们一直过着群居的生活,这样就有了雁群,每一只大雁都有自己的雁群,每个雁群都有好多大雁,大雁与雁群的这种关系就可以称之为聚合

另外每只大雁都有两只翅膀,大雁与雁翅的关系就叫做组合

有此可见:

聚合的关系明显没有组合紧密,大雁不会因为它们的群主将雁群解散而无法生存;

而雁翅就无法脱离大雁而单独生存——组合关系的类具有相同的生命周期。

聚合关系图:

UML中类之间的五种关系及其在C++代码中的表现形式

组合关系图:

UML中类之间的五种关系及其在C++代码中的表现形式

 在代码中表现如下:

//聚合
#include<bits/stdc++.h>
using namespace std;

class Goose{
public:
};

class GooseGroup{
public:
    Goose *goose;
    GooseGroup(Goose *g){
        goose = g;
    }
};


int main(){

}

 

//组合
#include<bits/stdc++.h>
using namespace std;

class Goose{
public:
};

class GooseGroup{
public:
    Goose *goose;
    GooseGroup(){
        goose = new Goose();
    }
};


int main(){

}

【解释】

这两种关系的区别是:

1.构造函数不同

  • 聚合类的构造函数中包含另一个类的实例作为参数

因为构造函数中传递另一个类的实例,因此大雁类可以脱离雁群类独立存在。

  • 组合类的构造函数包含另一个类的实例化

因为在构造函数中进行实例化,因此两者紧密耦合在一起,同生同灭,翅膀类不能脱离大雁类存在。

2.信息的封装性不同

在聚合关系中,客户端可以同时了解GooseGroup类和Goose类,因为他们是独立的。

在组合关系中,客户端只认识大雁类,根本不知道翅膀类的存在,因为翅膀类被严密地封装在大雁类中。

5.泛化(Generalization)

泛化是学术名称,通俗的来讲,通常包含类与类之间的继承关系和类与接口实现关系。

类与类之间的泛化

UML中类之间的五种关系及其在C++代码中的表现形式

接口的实现

UML中类之间的五种关系及其在C++代码中的表现形式

上一篇:C++笔记:奇葩排序之猴子排序、珠排序、面条排序


下一篇:【GAMES101-现代计算机图形学课程笔记】Lecture 01 Overview of Computer Graphics