类图基本属性
类图概括起来主要由两部分组成:类和类之间的关系,其中对类的定义如下图所示,主要由三部分组成,它们分别是类名、类的属性、类的方法,对应图中的三个分区内容。
类名:图中最上面的矩形框中为类名。如果字体为斜体,表示为抽象类
类的属性:类名下方的区域
类的方法:图中的下面部分
符号解释 说明:属性和方法前面的“+”“-”和“#”表示访问级别,以下对这些符号进行解释说明。
+:public,公用的,对所有类可见
-:private,私有的,只对该类本身可用
#:protected,受保护的,对该类的子孙可见
~:package,包的,只对同一包声明的其他类可见
=:表示默认值
下划线:static
斜体:抽象 (注意也可以用两个尖括号包裹来表示抽象,比如 —— <<我是抽象类or接口>>)
冒号前是方法名/变量名(根据有无括号区分),冒号后是返回参数/变量类型(根据有无括号区分),如果没有冒号的话表示方法返回空(也有人通过:void表示返空)
类之间的关系
类之间的关系主要包括泛化(继承)、依赖、关联、聚合、组合和实现6种关系,下面对它们进行一一阐释。
1、依赖关系
依赖关系表示一个类使用(依赖)另一个类的服务或信息。当一个类的改变会影响到另一个类时,两个类之间存在依赖关系。一般来说,依赖总是单向的,不应该存在双向依赖。表示方法:尖括号+虚线
依赖关系在C++中的表现就是:类A的对象使用类B的对象,其中类B的对象作为类A的方法参数、局部变量,或者直接在类A中调用类B的静态方法。
【箭头及指向】:在UML类图中,以虚线箭头表示,箭头从使用类指向被依赖的类。
体现:
类B作为类A的成员函数参数。
类B作为A的成员函数的局部变量
类A的成员函数调用的类B的静态方法
代码:
#include<iostream>
using namespace std;
class B
{
public:
static void showB()
{
cout << "showB" << endl;
}
};
class A
{
public:
void CallB1()
{
B::showB();
return;
}
void CallB2(B b)
{
b.showB();
return;
}
void CallB3()
{
B b;
b.showB();
return;
}
};
int main()
{
B b;
A a;
a.CallB1();
a.CallB2(b);
a.CallB3();
return 0;
}
//运行结果:
//showB
//showB
//showB
2、关联关系
关联关系是一种拥有的关系,它使一个类知道另一个类的属性和方法。它体现不同类的一种强依赖关系,比如我和我的朋友,这种关系比依赖更强,不存在依赖关系中的偶然性,关系也不是临时的,一般是长期性的。
关联关系分为单向关联或双向关联,也可以有多重性(一对多),双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。表示方法:尖括号+实线,箭头从使用类指向被关联的类。可以是单向和双向。
关联关系分为单向关联与双向关联,在Java中的表现是:一个类中包含有另一个类。
单向关联:类A中包含有类B。
双向关联:类A中包含有类B,类B中包含有类A。
【箭头及指向】:在UML类图中,以实线箭头表示,箭头指向被关联的类。
体现:
被关联类B以类的属性形式出来在关联类A中。
关联类A引用了一个类型为被关联类B的全局变量。
代码:
#include<iostream>
using namespace std;
//A和B是关联关系:
class Me;
class You
{
private:
Me* pm;
public:
void printMe();
void printYou();
};
class Me
{
private:
You* py;
public:
void printme();
void printyou();
};
void You::printMe()
{
cout << "You:" << endl;
pm->printme();
return;
}
void You::printYou()
{
cout << "我中有你" << endl;
return;
}
void Me::printyou()
{
cout << "Me:" << endl;
py->printYou();
return;
}
void Me::printme()
{
cout << "你中有我" << endl;
return;
}
int main()
{
Me zzh;
zzh.printyou();
zzh.printme();
You xxx;
xxx.printMe();
xxx.printYou();
return 0;
}
//
//Me:
//我中有你
//你中有我
//You :
//你中有我
//我中有你
3、聚合关系
从定义上来看,聚合关系是关联关系的一种,也是一个类中包含有另一个类,但是聚合关系强调的是“整体-个体”之间的相互关系,例如计算机与硬盘之间的关系,计算机作为一个整体,包含有硬盘,但是硬盘可以脱离计算机这个整体而单独存在。
【箭头及指向】:在UML类图中,以带空心菱形的实线表示,菱形从局部指向整体。
体现:
聚合通过成员对象来实现的。
聚合需要配合语义,结合上下文才能判断出是否为聚合,无法直接判断。
代码:
#include<iostream>
using namespace std;
class Husband;
class Wife;
class House
{
private:
string name;
public:
House(string s="家"):name(s){}
void showname()
{
cout << "家:" << name << endl;
}
};
class Husband
{
private:
House h;
string name;
public:
Husband(string s1, string s2="zzh") :h(s1),name(s2)
{
}
void showHouse()
{
h.showname();
cout << "丈夫" << name << endl;
}
};
class Wife
{
private:
House h;
string name;
public:
Wife(string s1, string s2 = "xxx") :h(s1), name(s2)
{
}
void showHouse()
{
h.showname();
cout << "妻子:"<<name << endl;
}
};
int main()
{
Husband me("home");
Wife who("Home");
me.showHouse();
who.showHouse();
return 0;
}
//运行结果:
//家:home
//丈夫zzh
//家 : Home
//妻子 : xxx
4、组合关系
从定义上来看,组合关系是关联关系的一种,也是一个类中包含有另一个类,但是组合关系强调的是“整体-部分”之间的共存亡关系,整体与部分具有相同的生命周期,部分不能与脱离整体而单独存在,有种唇亡齿寒的感觉哈。
【箭头及指向】:在UML类图中,以带实心菱形的实线表示,菱形从局部指向整体。
体现:
同聚合关系,组合也是通过成员对象来实现的。
组合也需要配合语义,结合上下文才能判断出是否为聚合,无法直接判断。
聚合和组合的最关键区别是两个对象的生命周期是否一致,而不是出现的形式。
代码:
#include<iostream>
using namespace std;
class eye
{
private:
double lefteyesight;
double righteyesight;
public:
eye(double d1, double d2) :lefteyesight(d1), righteyesight(d2)
{
}
void printeyesight()
{
cout << "左眼:" << lefteyesight << endl;
cout << "右眼:" << righteyesight << endl;
return;
}
};
class person
{
private:
eye e;
int sex;
public:
person(int x=0, double d1 = 1.0, double d2 = 1.0) :e(d1, d2), sex(x){}
void printperson()
{
cout << "性别:" << sex << endl;
e.printeyesight();
return;
}
};
int main()
{
person p;
p.printperson();
return 0;
}
//运行结果:
//性别:0
//左眼 : 1
//右眼 : 1
5、实现关系
实现关系是一种类与接口的关系,表示类是接口所有特征和行为的实现。表示方法:空心三角+虚线
实现关系在Java中表现的是类与接口之间的关系,接口中定义了抽象的特性,类则是该接口特性的具体实现。
【箭头及指向】:在UML类图中,以带三角箭头的虚线表示,箭头从实现类指向接口。
体现:
接口通过纯虚函数来实现,多态就是通过虚函数来实现的。
代码:
#include<iostream>
using namespace std;
class person
{
public:
virtual void printperson() = 0;
};
class student:public person
{
private:
string name;
public:
student(string s="zzh") :name(s) {}
void printperson()
{
cout << "这是一个人" << endl;
cout << "是学生,名字是:" << name << endl;
}
};
int main()
{
student s;
s.printperson();
return 0;
}
//运行结果:
//这是一个人
//是学生, 名字是:zzh
6、泛化关系
泛化关系是一种继承关系,子类继承父类的所有行为和属性,子类可以新增新的功能或者重写父类功能。表示方法:空心三角+实线,箭头指向父类
继承关系在Java表示的类与类(接口与接口)之间的父子关系,表示子类在具有父类特性的同时,还具有自己独特的特性。在Java中,用extends关键字表示继承关系。
【箭头及指向】:在UML类图中,以带三角箭头的实线表示,箭头从子类指向父类。
体现:
通过面向对象的继承机制实现继承关系。
代码:
#include<iostream>
using namespace std;
class person {};
class student :public person {};
这几种类与类之间的关系,耦合度也是依次增强的:依赖 < 关联 < 聚合 < 组合 < 继承 < 实现。
聚合和组合的区别
这两个比较难理解,重点说一下。聚合和组合的区别在于:聚合关系是“has-a”关系,组合关系是“contains-a”关系;聚合关系表示整体与部分的关系比较弱,而组合比较强;聚合关系中代表部分事物的对象与代表聚合事物的对象的生存期无关,一旦删除了聚合对象不一定就删除了代表部分事物的对象。组合中一旦删除了组合对象,同时也就删除了代表部分事物的对象。
类图模板案例
为了帮助大家更好的理解类之间的6种关系,下面使用例子辅助大家学习和消化吸收。
1、汽车类图案例
汽车类图说明:
车与小汽车和自行车之间是「实现」 关系,使用带空心箭头的虚线表示;
小汽车与SUV之间的关系为泛化关系,使用带空心箭头的实线表示;
小汽车与发动机和轮胎之间是「组合」 关系,使用实心菱形箭头的实线表示;
学生上学需要用到自行车,与自行车是一种「依赖」 关系,使用带箭头的虚线表示。
学生与班级之间是「聚合」 关系,使用带空心菱形箭头的实线表示;
学生与身份证之间为「关联」 关系,使用尖箭头的实线表示;
2、动物UML类图案例