一文搞懂 UML 类图!!!_uml类图-程序员宅基地

技术标签: uml  设计模式  

面向对象设计 主要就是使用UML的类图,类图用于描述系统中所包含的类以及它们之间的相互关系,帮助人们简化对系统的理解,它是系统分析设计阶段的重要产物,也是系统编码和测试的重要模型依据。

一、UML类图简介

    统一建模语言 UML (Unified Modeling Language) 类图是一种用于描述系统结构的图形化工具。它以类和对象为基础,主要用于表示系统中的类、接口、继承关系、关联关系等元素,以及它们之间的静态结构和关系。在本文中,将深入介绍UML类图的基本元素关系类型以及如何创建一个简单而有效的类图。

    类图以反映类的结构(属性、操作)以及类之间的关系为主要目的,描述了软件系统的结构,是一种静态建模方法。类图用来描述系统中有意义的概念,包括具体的概念、抽象的概念、实现方面的概念等,是对现实世界中事物的抽象

    类图的主要作用是对系统的词汇进行建模、对简单的协作进行建模和对逻辑数据库模式进行建模。类图显示集合的类,接口,关联,协作和约束,它也被称为作为结构图。

UML类图是在 设计程序之前 画,而不是等写完程序再画!!!

在这里插入图片描述

  • Java 中有接口
  • C++ 中没有接口,只有抽象类

二、类的UML画法

使用工具

    Visio 或者 processon 在线作图。
在这里插入图片描述

在类图中一共包含了以下几种模型元素,分别是:Class)、接口Interface)以及类之间的关系

    class / struct)封装了数据行为,是面向对象的重要组成部分,它是具有相同 属性操作关系对象集合总称,是 对具有相同属性和行为的对象的一种抽象表示。在系统中,每个类都具有一定的职责,职责指的是类要完成什么样子的功能,要承担什么样子的义务。一个类可以有多种职责,但是设计得好的类一般只有一种职责。

    接口Interface)是一种特殊的类,具有类的结构但 不可被实例化,只可以被实现继承)。在某种程度上,一个 包含纯虚函数的抽象类 可以被认为是一个接口。然而,严格来说,C++中没有显式的“接口”关键字,而是通过抽象类和纯虚函数的组合 来实现 接口的概念

    比如,我现在定义了猎人类:

class Hunter
{
    
public:
    int m_age = 32;		//普通的成员变量,属于对象,只有创建对象才会存在
    static int m_times; //静态成员变量,属于类,并不属于对象,在不创建对象的情况下仍然存在。(所有对象共享唯一的静态成员变量)
    
    string getName()
    {
    
        return m_name;
    }
    void setName(string name)
    {
    
        m_name = name;
    }

    void goHunting()
    {
    
        aiming();
        shoot();
    }
    static void saySorry() //静态成员函数,属于类,并不属于对象;可以直接通过类名调用
    {
    
    	//静态成员函数只能使用静态成员变量,非静态成员变量是不能直接使用的(但可以间接使用)
        string count = to_string(m_times);
        cout << "Say sorry to every animal " + count + " times!" << endl;
    }

protected:
    string m_name = "Jack";
    void aiming()
    {
    
        cout << "使用" + m_gunName + "瞄准猎物..." << endl;
    }

private:
    string m_gunName = "AK-47";
    void shoot()
    {
    
        cout << "使用" + m_gunName + "射击猎物..." << endl;
    }
};
int Hunter::m_times = 3;

    上面这个类对应的类图应该是这样的:
在这里插入图片描述

  • 类名:图中 最上面 的矩形框中为类名。(如果字体为 斜体,表示为 抽象类)
  • 类的属性类名下方的区域。
    • 格式:[可见性] [属性名称] : [类型] = { 默认值,可选 }
  • 类的方法:图中的下面部分
    • 格式:[可见性] [方法名称] ([参数名 : 参数类型, …]) : [返回值类型]

冒号 : 前是 方法名/变量名(根据有无括号区分)
冒号后 :返回参数/变量类型(根据有无括号区分)
如果没有冒号的话表示 方法返回空(也有人通过:void表示返空)

符号解释

  • 说明:属性和方法 前面的 “+” “-” 和 “#” 表示 可见性
    • +public公用的,对 所有类 可见
    • -private私有的只对 该类本身 可用
    • #protected受保护的,对该类的 子孙 可见
    • 不带符号:表示 default
可见性 符号 当前类 当前包 子孙类 其他包
public +
protected #
default
private -
  • 其他解释说明:
    • ~package包的,只对同一包声明的其他类可见
    • =表示默认值
    • __(下划线):表示static
    • 斜体抽象 (注意也可以用两个尖括号包裹来表示抽象,比如 —— <<我是 抽象类 or 接口 >>)

    如果我们定义的类是一个 抽象类(类中有纯虚函数),在画UML类图的时候,类名需要使用斜体显示

在这里插入图片描述

在使用UML画类图的时候,虚函数 的表示方式跟随类名,也就是使用斜体如果是 纯虚函数 则需要在最后给 函数指定 = 0

三、类与类之间的关系

    UML中的关系是面向对象关系。如果不以面向对象的思维去考虑会感觉到有很多关系认为是一样的。

关系 说明 表示
继承(泛化) 继承关系,指向方(符号左)子类,被指向方(符号右)为父类。 在这里插入图片描述
实现 接口的实现关系,指向方(符号左)实现类,被指向方(符号右)为接口类。 在这里插入图片描述
组合 整体和部分关系,指向方(符号左)整体类,被指向方(符号右)为部分类。 在这里插入图片描述
聚合 整体和部分关系,指向方(符号左)整体类,被指向方(符号右)为部分类。 在这里插入图片描述
关联 是类与类之间的联结,将一个类的对象(被指向方)作为另一个类(指向方)的属性。 在这里插入图片描述
依赖 使用关系,指向方(符号左)使用类,被指向方(符号右)为被使用类。 在这里插入图片描述

3.1 继承(泛化)关系

    继承 也叫作 泛化Generalization),用于描述父子类之间的关系,父类又称为 基类 或者 超类子类又称作 派生类。在UML中,泛化关系带空心三角形的实线 来表示。

    关于继承关系一共有两种:普通继承关系抽象继承关系,但是不论哪一种表示继承关系的线的样式是不变的

    假如现在我定义了一个父类Bird)和两个子类ParrotEagle):

class Bird
{
    
public:
    string getName()
    {
    
        return m_name;
    }
    void setName(string name)
    {
    
        m_name = name;
    }

    virtual void fly() {
    }
    virtual void eat() {
    }
    
protected: //可以被子类继承
    string m_name;
};

class Parrot : public Bird
{
    
public:
    void fly() override
    {
    
        cout << "我拍打翅膀飞行..." << endl;
    }
    void eat() override
    {
    
        cout << "我喜欢吃肉肉的小虫子..." << endl;
    }
};

class Eagle : public Bird
{
    
public:
    void fly() override
    {
    
        cout << "我展翅翱翔..." << endl;
    }

    void eat() override
    {
    
        cout << "我喜欢吃小动物..." << endl;
    }
};
  • 使用UML表示上述这种关系应当是:

在这里插入图片描述

  • 父类 Bird 中的 fly()eat()虚函数,它有两个子类PorrotEagle在这两个子类中重写了父类的虚函数,在使用 带空心三角形的实线 表示继承关系的时候,有空心三角的一端指向父类,另一端连接子类

3.2 组合关系

    组合Composition)关系也表示的是一种整体和部分的关系,但是在组合关系中 整体对象 可以 控制 成员对象的生命周期,一旦整体对象不存在,成员对象也不存在,整体对象和成员对象之间具有 同生共死 的关系。

在UML中组合关系用带 实心菱形的直线 表示,下面举两个组合关系的例子:

  • Head)和 嘴巴(Mouth)、鼻子(Nose)、耳朵(Ear)、眼睛(Eye
  • Tree)和 树根(Root)、树干(Trunk)、树枝(Branch)、树叶(Leaf

以树为例,对应的C++类的定义如下:

class Root
{
    
};

class Trunk
{
    
};

class Branch
{
    
};

class Leaf
{
    
};

class Tree
{
    
public:
    Tree()
    {
    
        m_root = new Root;
        m_trunk = new Trunk;
        m_branch = new Branch;
        m_leaf = new Leaf;
    }
    ~Tree()
    {
    
        delete m_root;
        delete m_trunk;
        delete m_branch;
        delete m_leaf;
    }
private:
    Root* m_root;
    Trunk* m_trunk;
    Branch* m_branch;
    Leaf* m_leaf;
};

其UML的表示方法为:
在这里插入图片描述

  • 代码实现组合关系,通常 在整体类的 构造方法直接实例化成员类,因为组合关系的整体和部分是共生关系整体的实例对象被析构 的时候它的 子对象也会一并被析构。如果通过外部注入,即使整体不存在了,部分还是存在的,这样的话就变成聚合关系了。

3.3 聚合关系

    聚合Aggregation)关系表示 整体部分 的关系。在聚合关系中,成员对象整体的一部分但是成员对象可以脱离整体对象独立存在。在UML中,聚合关系用 带空心菱形的直线 表示,下面举两个聚合关系的例子:

  • 汽车Car)与 引擎(Engine)、轮胎(Wheel)、车灯(Light
  • 森林Forest)与 植物(Plant)、动物(Animal)、水(Water)、阳光(Sunshine

以森林为例,对应的C++类的定义如下:

class Plant
{
    
    // 植物
};

class Animal
{
    
    // 动物
};

class Water
{
    
    // 水
};

class Sunshine
{
    
    // 阳光
};

//森林
class Forest
{
    
public:
    Forest(Plant p, Animal a, Water w, Sunshine s) : m_plant(p),m_animal(a),m_water(w),m_sun(s)
    {
    
    }
private:
    Plant m_plant;
    Animal m_animal;
    Water m_water;
    Sunshine m_sun;
};

对应的UML类图为:
在这里插入图片描述

  • 代码实现聚合关系成员对象 通常以 构造方法Setter方法的方式注入到 整体对象 之中,因为成员对象可以 脱离整体对象 独立存在

表示聚合关系的线,由 空心菱形 的一端指向整体对象,另一端连接 局部对象(有些UML绘图软件在这一端还带一个箭头)。

3.4 关联关系

     关联Assocition)关系是类与类之间最常见的一种关系,它是一种结构化的关系,表示一个对象与另一个对象之间有联系,如汽车和轮胎、师傅和徒弟、班级和学生等。在UML类图中,用 带接头不带箭头 的)实线 连接有关联关系的类。在C++中这种关联关系在类中是这样体现的,通常 将一个类的对象 作为 另一个类的成员变量

    类之间的关联关系有三种,分别是:单向关联双向关联自关联。下面逐一给大家进行介绍。

(a) 单向关联关系

    单向关联指的是关联只有一个方向,比如每个孩子Child)都拥有一个父亲Parent),其代码实现为:

class Parent
{
    
};

class Child
{
    
private:
    Parent m_father;
};
  • 通过UML来说描述这两个类之间的关系,如下图:

在这里插入图片描述

  • 如果是单向关联,使用的连接线是 带单向箭头的实线 , 哪个类作为了当前类的成员变量,那么箭头就指向哪个类。在这个例子中 Father 类 作为了Child 类成员变量,因此箭头端应该指向 Father 类 ,另一端连接Child 类
(b) 双向关联关系

    现实生活中每个孩子都有父母,每个父母同样自己的孩子,如果想要通过类来描述这样的亲情关系,代码如下:

class Parent
{
    
private:
    Child m_son;
};

class Child
{
    
private:
    Parent m_father;
};
  • 通过UML来说描述这两个类之间的关系,如下图:

在这里插入图片描述

  • 在画UML类图的时候,一般使用 没有箭头的实线 来连接有双向关联关系的两个类,这两个类的对象分别作为了对方类的成员变量。

有些UML绘图软件使用的是 带双向箭头的实线 来表示双向关联关系。

在这里插入图片描述

(c ) 自关联关系

    自关联指的就是当前类中 包含一个自身类型的对象成员,这在 链表 中非常常见,单向链表中都会有一个指向自身节点类型的后继指针成员,而双向链表中会包含一个指向自身节点类型的前驱指针和一个指向自身节点类型的后继指针。就以双向链表节点类为例,它的C++写法为:

class Node 
{
    
private:
    int m_data = 0;
    Node* m_prev;
    Node* m_next;
};

对应的UML类图应当是:
在这里插入图片描述

  • 一般使用 带箭头的实线 来描述自关联关系,我中有我,独角戏

有些UML绘图软件表示类与类的关联关系,使用的就是一条实线,没有箭头

3.5 依赖关系

    依赖Dependency)关系是一种 使用关系,特定事物的改变有可能会影响到使用该事物的其他事物,在需要表示一个事物使用另一个事物时使用依赖关系,大多数情况下依赖关系体现在某个类的方法 使用 另一个类的对象 作为参数。(也可以使用排除法判断,不是组合和聚合关系,就是依赖联系啦!

    在UML中,依赖关系带箭头的虚线 表示,由 依赖 的一方 指向 被依赖的一方,下面举两个依赖关系的例子:

驾驶员(Driver)开车,需要将车(Car)对象作为参数传递给 Driver 类drive()方法。

class Car 
{
     
public: 
    void move() {
    }
}; 

class Driver 
{
    
public: 
    void drive(Car car) 
    {
     
        car.move(); 
    } 
};

树木(Tree)的生长,需要将空气(Air)、水(Water)、土壤(Soil)对象作为参数传递给 Tree 类grow()方法。

class Water
{
    
};

class Air
{
    
};

class Soil
{
    
};

class Tree
{
    
public:
    void grow(Water w, Air a, Soil s) 
    {
    
        cout << "借助 w 中的水分, s 中的养分和 a 中的二氧化碳, 我就可以茁壮成长了";
    }
};

关于树木这个类,它对应的UML类图为:
在这里插入图片描述
依赖关系通常通过三种方式来实现:

  1. 将一个类的对象 作为 另一个类中方法的参数
  2. 在一个类的方法中将另一个类的 对象 作为 其对象的 局部变量
  3. 在一个类的方法中 调用 另一个类的 静态方法

类之间的关系强弱顺序是这样的:继承(泛化) > 组合 > 聚合 > 关联 > 依赖

组合、聚合、关联关系之间的区别

  • 组合聚合 的区别则在 语义实现 上都有差别:

    • 组合的两个对象之间生命周期有很大的关联,被组合的对象组合对象创建的 同时或者创建之后 创建在组合对象销毁之前销毁聚合无需考虑这些事情
    • 一般来说 被组合对象 不能脱离 组合对象独立存在,而且也只能属于 一个 组合对象,聚合则不一样,被聚合的对象可以属于 多个 聚合对象
  • 关联聚合 的区别主要在于 语义 上:关联的两个对象之间一般是平等的,聚合则一般是不平等的。


(实际应用中,这三种关系的界限划分其实没有那么清楚,有些时候我们会感觉组合和聚合没什么区别,所以,在设计的时候没必要死抠细节,只要能够利用对象之间的关系设计出可行的解决方案即可。 如果同时有多个关系,只需画出最强的关系即可。)

最后,再举例子来描述一下这三种关系:

  • 人和自己的心脏属于组合关系,因为心脏不能脱离人体而独自存在。
  • 图书馆看书的时候,人和书属于聚合关系。书是可以独立存在的,而且书不仅可以属于自己,也可以属于别人。
  • 朋友之间属于关联关系,因为这种关系是平等的,关联关系只是用于表示两个对象之间的一种简单的联系而已。

注释 使用右上角的 带三角折痕的矩形加虚线 来表示注释。
在这里插入图片描述

参考:https://subingwen.cn/design-patterns/UML-class-diagrams/

注:仅供学习参考,如有不足,欢迎指正!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_43412762/article/details/134677819

智能推荐

Zabbix的介绍和部署_zabbix网络中心-程序员宅基地

文章浏览阅读707次。zabbix agent安装在被监控的主机上,zabbix agent负责定期收集客户端本地各项数据,并发送至 zabbix server 端,zabbix server 收到数据后,将数据存储到数据库中,用户基于 Zabbix WEB 可以看到数据在前端展现图像。_zabbix网络中心

linux网络下载_liunx网上下载-程序员宅基地

文章浏览阅读372次。网站下载wget 是一个用于文件下载的命令行工具用wget可以下载网页或远程文件:$ wget URL多处URL下载$ wget URL1 URL2 URL3..下载文件输出文件到指定文件$ wget ftp://example.com/somefile.img -o dloaded_file.img -o log指定重试次数$ wget -t 5 URL使用--limit-rate对wget限速$ wget --limit-rate 20k http://.....可以使用k和m指_liunx网上下载

架构师必读 : 微服务架构设计指南-程序员宅基地

文章浏览阅读109次。每个人都听说过微服务。但你知道怎么设计吗? 微服务是当今软件工程师的一个热门话题。让我们了解如何使用微服务架构风格构建真正模块化、业务敏捷的IT系统。一、微服务概念微服务体系结构由轻量级、松散耦合的服务集合组成。每个服务都实现了单个业务功能。理想情况下,这些服务应该是具有足够的内聚性,可以独立地开发、测试、发布、部署、扩展、集成和维护。正式定义 “微服务架构风格是一种将单个应用程序开发为一组小型..._微服务工程师 百度百科

2023-2024年人形机器人行业报告合集(精选397份)_高工 人形机器人报告-程序员宅基地

文章浏览阅读710次,点赞22次,收藏17次。2024人形机器人力传感器行业研究报告:人形机器人商业化图景远大,引爆六维力传感器市场空间。2024机器人行业研究报告:英伟达赋能机器人AI超预期,二季度迎机器人定点最大催化。2024人形机器人的Optimus时刻报告:IMU(姿态感知),旧火新茶,其时已至。2024人形机器人报告:MEMS IMU或为人形机器人实现两足运动平衡的最佳方案。2024人形机器人丝杠报告:丝杠作为人形机器人核心传动部件,正面临新一轮产业机遇。2024人形机器人精密减速器报告:机器人核心部件有望持续受益人形机器人产业带动。_高工 人形机器人报告

【史上最易懂】马尔科夫链-蒙特卡洛方法:基于马尔科夫链的采样方法,从概率分布中随机抽取样本,从而得到分布的近似_马尔科夫链期望怎么求-程序员宅基地

文章浏览阅读1.3k次,点赞40次,收藏19次。虽然你不能直接计算每个房间的人数,但通过马尔科夫链的蒙特卡洛方法,你可以从任意状态(房间)开始采样,并最终收敛到目标分布(人数分布)。然后,根据一个规则(假设转移概率是基于房间的人数,人数较多的房间具有较高的转移概率),你随机选择一个相邻的房间作为下一个状态。比如在巨大城堡,里面有很多房间,找到每个房间里的人数分布情况(每个房间被访问的次数),但是你不能一次进入所有的房间并计数。但是,当你重复这个过程很多次时,你会发现你更有可能停留在人数更多的房间,而在人数较少的房间停留的次数较少。_马尔科夫链期望怎么求

linux以root登陆命令,su命令和sudo命令,以及限制root用户登录-程序员宅基地

文章浏览阅读3.9k次。一、su命令su命令用于切换当前用户身份到其他用户身份,变更时须输入所要变更的用户帐号与密码。命令su的格式为:su [-] username1、后面可以跟 ‘-‘ 也可以不跟,普通用户su不加username时就是切换到root用户,当然root用户同样可以su到普通用户。 ‘-‘ 这个字符的作用是,加上后会初始化当前用户的各种环境变量。下面看下加‘-’和不加‘-’的区别:root用户切换到普通..._限制su root登陆

随便推点

信息通信服务、电子商务及物流服务的创新与发展_信息通信,电子商务-程序员宅基地

文章浏览阅读828次。2019年,国际互联网的蓬勃发展促使“物联网”(IoT)、云计算、大数据、人工智能等新兴技术的普及和应用。而在物流、电子商务、信息通信网络服务领域,亦或将成为信息时代最重要的基础设施。近几年,数字经济正走向成熟,用户的接受能力也越来越高,因此,信息通信服务、电子商务及物流服务都迎来了新的机遇。这些领域正经历着蓬勃的创新变革和不断变化,也是非常值得关注的领域。2020年,我国在推进“一带一路”倡议、开放世界经济格局方面取得重大成功,也促进了互联网和电子商务的发展。_信息通信,电子商务

websocket.js的封装,包含保活机制,通用_websocket保活-程序员宅基地

文章浏览阅读774次。websocket的封装,包含保活机制_websocket保活

Ubuntu安装conda-程序员宅基地

文章浏览阅读2.6w次,点赞14次,收藏116次。期间有ENTER的地方可以直接回车,遇到MORE信息,可以摁Q键跳过,遇到需要输入yes|no的地方输入yes即可。即可直接从清华镜像网站上下载anaconda安装包,视情况选择自己的版本,我选择的是2021.11版本。3.安装完成后关闭终端重新打开终端,输入conda--version。在ubuntu中ctr+alt+t打开终端,输入。1.下载Anaconda安装包。2.安装包下载完成之后键入。如果有版本输出则安装成功。......_ubuntu安装conda

LoadRunner性能测试关注指标及结果分析_loadrunner性能指标分析-程序员宅基地

文章浏览阅读2.2w次,点赞11次,收藏97次。首先感谢原博主的分享,这是原博客地址:http://www.51testing.com/?uid-562021-action-spacelist-type-blog-itemtypeid-26819原文: LoadRunner性能测试结果分析是个复杂的过程,通常可以从结果摘要、并发数、平均事务响应时间、每秒点击数、业务成功率、系统资源、网页细分图、Web服务器资源、数据库服务器资源等几个_loadrunner性能指标分析

java怎么做图形界面_java怎么做图形界面?实例分享-程序员宅基地

文章浏览阅读6.8k次,点赞3次,收藏29次。学习java不仅要学会写程序,也要学会做图形界面,可是做图形界面对于一些刚学java的人员来说还是比较困难的,那么今天我们就给大家分享一下java做图形界面的方法。首先我们来了解一下创建图形界面时常见的组件类和辅助类。(1)容器组件类:容器上能添加其他的组件,那么该组件就是容器组件。如果要实现顶级容器,首先要有一个顶级容器。JFrame 窗体容器组件类(2)元素组件类:一般是用来显示文字,图片或者..._java编写图形界面步骤

eMMC常识性介绍N_emmc温升系数-程序员宅基地

文章浏览阅读4.1k次。原文地址:eMMC常识性介绍N作者:35后时代摘自网络 http://www.up48.com/news.htm eMMC使用厂商 目前针对全球主要手机大厂如诺基亚(Nokia)、三星电子(SamsungElectronics)、摩托罗拉(Motorola)、黑莓(RIM)和乐金电子(LGElectronics)等均已在智能手机或者3G手机等高端产品全面采用e_emmc温升系数

推荐文章

热门文章

相关标签