《小酌重构系列》已经完成了大约1/3了,在这些文章中,我使用了一些简单的类图来描述重构策略。在之后的文章中,我可能会借助稍微复杂一些的UML类图来介绍。但是在此之前,我觉得有必要先介绍一下UML类图中6大关系了。这6大关系分别是Inheritance(继承)、Implementation(实现)、Dependency(依赖)、Association(关联)、Aggretation(聚合)和Composition(组合)。在这6大关系中,依赖、关联、聚合和组合是比较容易混淆的,我也会讲解它们之间的区别。

UML类图简介

UML是Unified Modeling Language的缩写,翻译为中文是“统一建模语言”,它是面向对象软件的标准化建模语言。它是一个支持模型化和软件系统开发的图形化语言,为软件开发的所有阶段提供模型化和可视化支持,包括由需求分析到规格,到构造和配置。

不同组织不同软件对UML的种类划分是不同的,基本所有的UML的软件都包含了以下4大种类

1. 用例图
2. 类图
3. 活动图
4. 序列图

UML类图

UML类图用于展现了一组对象、接口、协作和它们之间的关系。类图描述的是一种静态关系,在系统的整个生命周期都是有效的,是面向对象系统的建模中最常见的图。

UML工具

市面上UML的工具软件非常多,每个人的口味不同,我就不多做介绍了,我最常用的UML工具是Visio。


当然作为一个.NET开发者,Ultimate版本的Visual Studio也提供了UML建模功能。

继承和实现

继承(Inheritance)

Inheritance表示一个类(接口)继承另一个类(接口)的功能和属性,用于描述父类(接口)和子类(接口)之间的关系。
继承描述了"is a kind of "关系,例如:Manger是Employee的一种,Manager继承了Employee的所有功能(例如:刷卡签到、执行工作)和属性(例如:员工姓名、入职时间)。

在UML中,继承使用实线空心箭头表示,空心箭头指向父类(接口)。

实现(Implementation)

Implementation表示类实现接口的功能。

在UML中,继承使用虚线空心箭头表示,空心箭头指向接口。

继承和实现的区别

虽然在C#中继承和实现都使用符号:来表示(java中使用extends表示继承,implements表示实现),但二者还是有些区别的。

1. 继承发生在“类和类”或“接口和接口”之间,例如:子类继承父类,子接口继承父接口。

子类继承父类:

public abstract class Animal
{
} public class Bird : Animal
{ }

子接口继承父接口:

public interface ITransportation
{
void Move();
} public interface IVehicle : ITransportation
{ }

2. 实现发生在“类和接口”之间,例如:类实现某个接口的方法。

public interface IVehicle : ITransportation
{ } public class Car : IVehicle
{
public void Move()
{
Console.WriteLine("汽车跑起来...");
}
}

3. 在C#中,多继承确切地说是多实现。

不像C++语言的语法,C#的类不能同时继承多个类。C#仅能继承一个类,但可以同时实现多个接口。
例如:ASP.NET MVC中的Controller类,继承了ControllerBase类,同时实现了IActionFilter, IAuthenticationFilter…等接口。

public abstract class Controller : ControllerBase, IActionFilter, IAuthenticationFilter, IAuthorizationFilter,
IDisposable, IExceptionFilter, IResultFilter, IAsyncController, IAsyncManagerContainer
{
}

依赖、关联、聚合和组合

依赖(Dependency)

依赖关系是对象之间最基本的关系。
当一个对象(调用者)需要调用另外一个对象(被调用者)的方法去完成某些工作时,依赖关系就建立了。

在UML中,依赖关系使用虚线箭头表示,箭头指向被依赖的一方。

例如:在Web Service中,Client需要调用Service的操作,这就表示Client依赖于Service。

关联(Association)

Association表示多个对象之间的关联关系。
每个对象都有自己的生命周期,对象之间不存在从属关系。

在UML中,关联关系使用一条直线表示。

例如:Student和Teacher之间就属于”Association”,多个Student可以关联到一个Teacher,一个Student也可以关联到多个Teacher。
但是Teacher和Student之间没有“从属”或“包含”关系。

聚合(Aggregation)

Aggregation体现的是整体和部分之间的关系,即“has-a”关系。
整体和部分是可以分离的,即整体和部分都可以拥有各自的生命周期。

在UML中,聚合关系使用空心菱形箭头表示,箭头指向整体。

例如:一个Department拥有多个Employee,Department作为整体,Department中的Employee是Department的一部分。
Department和Employee都有自己的生命周期,当一个Department被撤销时,Employee可以转到其他Department或离职了。
Employee转到其他Department或离职时,Department仍然是存在的。

组合(Composition)

Composition体现的也是整体和部分之间的关系,即“is-a”关系。
组合关系是更为强力的聚合关系,整体和部分是不可以分离的。
整体的生命周期结束时,也意味着部分的生命周期结束。

在UML中,组合关系使用实心菱形箭头表示,箭头指向整体。

例如:一套房屋有多个房间,房间是房屋的一部分。房间的生命周期依赖于房屋的生命周期,当房屋被拆掉时,房间也就不存在了。

依赖、关联、聚合和组合的区别

依赖、关联、聚合和组合都可以泛指为”依赖关系”。
当对象之间构成Association、Aggregation或Composition关系时,也建立了对象之间的依赖关系。
它们表现的依赖关系强弱程度不同,这4种关系所表现的强弱程度依次为:Composite > Aggregation > Association > Dependency。

关联、聚合和组合是大家经常容易混淆的3种关系,这种关系最大的区别在于对象的生命周期。

1. 关联关系:每个对象都有自己的生命周期,对象之间不存在从属关系
2. 聚合关系:整体和部分是可以分离的,整体和部分都可以拥有各自的生命周期
3. 组合关系:整体和部分是不可以分离的,整体的生命周期结束时,也意味着部分的生命周期结束。

分清楚这些关系有必要吗?

今天有读者问到了一个问题:分清楚这些符号有必要吗?
我的回答是:因团队而异,这取决于团队沟通和交流的方式,也取决于团队成员的能力。
UML是一种沟通语言,你可以通过它模糊地表达一段内容,你也可以准确地描述这些内容,只要团队的其他成员能够领会你的意思。
沟通是以结果为向导的,你大可不必拘泥于沟通的方式,但在沟通过程中准确有效地表达尤其重要,这里的“准确”不是指准确地使用UML符号,而是指他人能准确地领会你所表达的内容。

我个人觉得:在涉及到系统中关键的模型时,用确切的符号来表达关系仍然是比较重要的。
例如:在一个采购系统中,拥有采购申请 → 采购订单这样一个流程。采购申请由用户选择商品、供应商后创建;采购订单由审核过的采购申请生成。
这里存在3对关系:
1. 采购申请和采购申请明细的关系
2. 采购订单和采购订单明细关系
3. 采购申请和采购订单之间的关系

从业务上看,1、2是一种组合关系,3是一种关联关系。如果笼统地将1、2、3理解为依赖关系,可能会产生一些问题。
在设计过程中,如果我们正确地描述了这3对关系,那么在删除单据时,可根据确切的关系归纳出以下行为:

1. 删除采购申请时,采购申请明同时被删除(因为它们是组合关系)
2. 删除采购订单时,采购订单明细同时被删除(因为它们是组合关系)
3. 删除采购订单时,不影响采购申请(因为它们是关联关系)

UML类图的6大关系的更多相关文章

  1. UML类图6种主要关系区别和联系

    UML类图关系图示,因为长得都很类似,所以大家总会混淆,本文主要目的就是分析一下6种主要的关系,找到联系与区别,便于记忆. 6种主要的关系如图1所示.继承与实现.组合与聚合.关联与依赖可分别划分为一组 ...

  2. uml类图的几种关系

    UML类图几种关系的总结   在UML类图中,常见的有以下几种关系: 泛化(Generalization),  实现(Realization),关联(Association),聚合(Aggregati ...

  3. UML类图的6中关系

    引用自: http://blog.csdn.net/tianhai110/article/details/6339565 UML类图分为如下四种关系: 1.  泛化 (Generalization)  ...

  4. UML类图中的六种关系(物理设计阶段)

    UML类图中经常会用到各种箭头和线条来表示不同类或者接口之间的关系,因此非常好的理解各个图标的含义是很有必要的. 在物理设计阶段可以通过EA工具将类图搭建好,然后直接生成物理类,这样也可以减少物理设计 ...

  5. UML类图的几种关系总结【转】

    在UML类图中,常见的有以下几种关系: 泛化(Generalization),  实现(Realization),关联(Association),聚合(Aggregation),组合(Composit ...

  6. UML类图的几种关系总结

    本文摘自:UML类图关系总结 在UML类图中,常见的有以下几种关系: 泛化(Generalization),  实现(Realization),关联(Association),聚合(Aggregati ...

  7. UML类图中的六种关系及实例

    前言: 设计模式是一种对于面向对象语言(C#,C++,Java)的高级应用.其思维体现出的是真正的代码设计.每一种模式都堪称巧妙!但基于各种设计模式,这里少不了基本的类图设计,本文简要列出6种关系,及 ...

  8. 【转】UML类图符号 6种关系说明以及举例

    转自http://www.cnblogs.com/duanxz/archive/2012/06/13/2547801.html UML中描述对象和类之间相互关系的方式包括:依赖(Dependency) ...

  9. UML类图中的六种关系及实例【补充】

    ·继承和接口都比较常见,通过继承子类可以直接使用父类的(public,protected属性以及方法:而实现了Speakable接口的Person类必须定义其所有方法,包括speak()): ·依赖指 ...

随机推荐

  1. 7 Container With Most Water_Leetcode

    Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). ...

  2. Beginning Scala study note(3) Object Orientation in Scala

    1. The three principles of OOP are encapsulation(封装性), inheritance(继承性) and polymorphism(多态性). examp ...

  3. 安装maven编译环境

    安装maven编译环境 1.默认已经装好yum并配置好yum源(推荐使用163yum源) 2.安装JDK 3.安装相关依赖环境(root用户登陆) yum install -y cmake lzo-d ...

  4. Go语言 Cookie的使用

    首先看看Cookie的结构体 type Cookie struct { Name string Value string Path string // optional Domain string / ...

  5. 安装SVN客户端重启电脑之后,右键未出现SVN选项的原因

    今天安装SVN客户端明明安装成功,电脑也重启过了,但是就是在右键里找不到SVN,百度了很久,删注册表.建用户组之类的方法都试过了,但是都没有效果. 后来才找到真正的原因,我的安装包是32位的,系统是6 ...

  6. Linux学习笔记(12)-进程间通信|匿名管道

    Linux的进程间通信有几种方式,包括,管道,信号,信号灯,共享内存,消息队列和套接字等-- 现在一个个的开始学习! ----------------------------------------- ...

  7. Gridview样式的CSS控制

    页面代码: .<asp:GridView ID="gvCustomres" runat="server" . DataSourceID="cus ...

  8. 使用Android Butterknife

    我之前浏览过android butterknife 的使用 在android studio 中,很惊喜,已经成为一个插件来使用 这个android butterknife 最大的用处,就是直接生成la ...

  9. JDBC

    <java连接数据库> Class.forName("com.mysql.jdbc.Driver")--1:加载驱动 Connection conn=DriverMan ...

  10. 面试题:给定数组a,找到最大的j-i, 使a[j]>a[i]

    第一种方法: 用两重循环对每对点都试一下,然后取最大值即可,时间复杂度为O(n2) #include <iostream> #include <algorithm> using ...