UML基础知识
什么是UML?
UML(Unified Modeling Language),即统一建模语言,是一种为面向对象系统的产品进行说明、可视化和编制文档的一种标准语言,是非专利的第三代建模和规约语言。UML是面向对象设计的建模工具,独立于任何具体程序设计语言。
在结构化开发方法中,系统分析的建模语言是数据流图,系统设计的建模语言是模块结构图。在面向对象开发方法中,系统分析与设计的建模语言是UML,UML仅仅是一种语言,它不是一种系统设计的方法,而是系统建模的标准。
UML本身并非是用于程序设计的语言,它本质上是一种表示法。拥有一种定义良好、富有表现力的表示法对软件开发的过程是很重要的。首先,标准的表示法让分析师和开发者能够描述一种场景,阐明一种架构,然后无二义地将这些决定告诉别人。这正如在电子工程和建筑领域一样,比如北京的建筑设计院设计师设计的房屋设计图纸,在广州的建筑施工团队基本上能够毫无困难地理解并依此开展施工。其次,正如Whitehead在他的数学名著中说的,好的表示法消除了大脑的不必要工作,让大脑能够集中考虑更高级的问题。最后,一种富有表现力的表示法能够利用自动化的工具,消除关于这些决定的血多繁琐的一致性和正确性检查。
UML是一种建模语言,是一种表示法,该表示法的可视化呈现是图,在UML里有各种图来对分析和设计的过程进行建模,但我们需要了解的是,画图的动作并不是分析或设计的要点,一张图示只是记录了系统行为的一种说明(对于分析来说)或一种架构的远景与细节(对于设计来说),如果遵循任何一种工程师(软件、电子、土木、建筑或其他)的工作方式,你很快会意识到系统构想形成的唯一场就是设计者的头脑。当这种设计随时间展开时,它常常被记录在一些高科技媒质上,就如同被记录在白板,笔记本甚至是餐巾纸上一般。
所以,UML是针对面向对象程序设计的一种建模语言,是一种表示法,但更重要的是背后的OOAD的思想,它的作用是用于设计者自己和相关干系人之间对系统分析和设计进行记录的一种语言,就如人和人之间的自然语言一般,它是对OOAD思想的一种表达工具。
UML视图(UML View)
UML中的视图也就是UML View,可以理解为审视系统的角度或维度,一个系统从不同的视角和维度来分析,就会得到不同的观察结果,就会得到不同的图。对一个复杂的系统来说,如果只是以一种视角来分析和观察,得到的结论将会是局部的,就如盲人摸象一样。
UML视图目前有不同的分类,如有的分类包括用例视图(Use Case View)、逻辑视图(Logical View)、并发视图(Concurrent View)、组件视图(Component View)、配置视图(Deployment View)等,不同的视图分别是从静态和动态、结构和行为等不同的维度和视角进行分析的产物。最常见的UML View的分类是分为如下的五个类别:用户视图(User's View)、结构视图(Structural Views)、行为视图(Behavioral Views)、环境视图(Environmental View)、实现视图(Implementation View)。每种视图又包含了不同的图的集合,具体情况如下所述。
用户视图(User's View)
包含用户与软件系统的交互,不包含软件系统内部的工作方式,该视图主要包含的图是:
- 用例图(use case diagram)
结构视图(Structural Views)
在结构视图中,说明了模型的结构,可以展示出软件系统的组成,但系统内部的工作方式仍然没有在该模型中定义,该视图包含的图有:
- 类图(Class Diagrams)
- 对象图(Object Diagrams)
行为视图(Behavioral Views)
行为视图包含了能够解释软件系统行为的图,具体包含:
- 序列图(Sequence Diagram)
- 协作图(Collaboration Diagram)
- 状态图(State chart Diagram)
- 活动图(Activity Diagram)
环境视图(Environmental View)
环境视图包含了解释软件模型部署后行为的图,该图通常说明用户交互和软件对系统的影响,环境模型包含的图:
- 部署图(Deployment diagram)
实现视图 (Implementation view)
实现视图由代表软件实施部分的图组成。此视图与开发人员的视角相关,这是定义软件内部工作流程的唯一视图。 该视图包含的图:
- 组件图(Component Diagram)
UML图(Diagram)
这部分是UML中较核心的部分,是分析和设计软件系统时的重要工具和表示方式。UML是一份详细的规范,但并不意味着在实际使用中每次都用到它的所有方面。实际上,这种表示法的一个子集足以表达大多数分析和设计问题中的语义。接下来我们就会对经常使用的这个图的子集进行阐述。
用例图(use case diagram)
用例图的作用:用例图在很大程度上用于软件系统的分析阶段,是辅助进行需求分析的有效手段。并且,用例图往往表达的并非是需求的详述,而是一种辅助,常常针对的是核心的成功场景。用例抽象的质量,会对后续进一步的分析和设计即OOAD的工作产生影响。
用例的关键概念
参与者(actor):某些具有行为的事物,可以是人(由角色标识),也可以是计算机系统或组织。参与者又分为主动参与者和被动参与者,用例图中一个良好的习惯是将主动参与者放在用例图的左边,而将被动参与者放在用例图的右边。
场景(Scenario):参与者和系统之间一系列特定的活动和交互。包括主成功场景和交替场景(或叫主路径和扩展路径);
系统边界:参与者是在系统外部,系统边界确定了目标系统的范围。
用例就是一组相关的成功和失败的场景集合,用例是问题域的描述,而非解决方案域的描述,所以不能有太多跟技术相关的术语,而应该是业务术语。
如上图所示为一个银行系统的用例图,其中cashier为收银员,椭圆形代表一个用例,用例之间存在一定的关系。
用例图中存在的关系
包含(include)关系:使用包含(Inclusion)用例来封装一组跨越多个用例的相似动作(行为片断),以便多个基(Base)用例复用。包含关系的主要作用在于复用,基用例和包含用例之间是必要的关系,当去掉包含用例时,基用例将不完整。在绘制用例图时,一定要注意包含(include)箭头的方向。
扩展(extend)关系:当某个新用例在原来的用例即基用例基础上增加了新的步骤序列,则这种关系称为扩展关系。可以这样理解,这里的基用例是一个完整的用例,即使没有子用例的参与,也可以完成一个完整的功能,只有当扩展点被激活时,子用例才会被执行。同样地,这里要注意基用例和扩展用例之间关系的箭头指向。
泛化(Generalization)关系:当多个用例共同拥有一种类似的结构和行为时,可以将他们的共性抽象成为父用例,其他的用例作为泛化关系的子例。在用例的泛化关系中,子用例是父用例的一种特殊形式,它继承了父用例的所有结构、行为、关系,用例图中的泛化关系与继承关系是等同的,其中三角箭头指向父用例。
如何发现用例
- 选择系统边界。
- 确定主要参与者
- 确定每个主要参与者的目标
- 定义满足用户目标的用例,并根据其目标对用例命名;
类图(class diagram)
类图是一个静态视图,它的核心包括类的表示法以及类之间关系的表示。
表示法
类的基本表示法,包括了名称、属性、方法;
属性定义的格式:[可视性]属性名[:属性类型],其中[]内为可选内容;
方法定义的格式:[可视性]操作名[(参数列表)][:返回类型][{特性}]
关键表示的意义:
- -表示private
- #表示protected
- ~表示default,也就是包权限
- _下划线表示static
- 斜体表示abstract
类之间的关系
在UML类图中,常见的有以下几种关系: 关联(Association),依赖(Dependency),聚合(Aggregation),组合(Composition),泛化(Generalization), 实现(Realization),并且这些关系中有时之间是有关联的,存在关系之间的强弱。
关联(Association):是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子关联可以是双向的,也可以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。代码层面的关系体现为成员变量。
依赖(Dependency):对于两个相对独立的对象,当一个对象负责构造另一个对象的实例,或者依赖另一个对象的服务时,这两个对象之间主要体现为依赖关系。又分为三种情况:当类A调用和实例化public的类B;类B的实例是类A某个方法的一个局部变量;当类B的实例是类A某个方法的一个入参。依赖关系应该是单向的。
聚合(Aggregation):是整体与部分的关系,且部分可以离开整体而单独存在,是一种弱的拥有关系,一般为“has a”的关系,两者各自具有各自的生命周期。聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。注意方向。
组合(Composition):组合是一种强的‘拥有’关系,是一种contains-a的关系,体现了严格的部分和整体关系,部分和整体的生命周期一样。如鸟和翅膀就是组合关系,因为它们是部分和整体的关系,并且翅膀和鸟的生命周期是相同的。
泛化(Generalization):是一种继承关系,表示一般与特殊的关系,它指定了子类如何特化父类的所有特征和行为。表示法为带三角箭头的实线,箭头指向父类;
实现(Realization):实现表示一个class类实现interface接口(可以是多个)的功能。比如大雁要飞翔,就要实现飞的接口。
序列图(Sequence Diagram)
顺序图是交互图的一种,表示的是一种动态的维度或视角,是一种强调消息时间顺序的交互图。所谓动态,强调对象之间的互相作用,随着时间的推移,一些对象被创建,属性值被改变,一些对象被销毁等等。
序列图将交互关系表示为一个二维图。纵向是时间轴,时间沿竖线向下延伸。横向轴代表了在协作中各独立对象的类元角色。类元角色用生命线表示。当对象存在时,角色用一条虚线表示,当对象的过程处于激活状态时,生命线是一个双道线。消息用从一个对象的生命线到另一个对象生命线的箭头表示。箭头以时间顺序在图中从上到下排列。
一个序列图用如下形状的图表示。
序列图中涉及的主要元素包括:
对象(Object):表示参与交互的实体,用方框加名字来表示。
生命线(Lifeline):生命线用竖直的虚线表示。
激活(Activation):代表时序图中对象执行一项操作的时期, 表示该对象被占用以完成某个任务,在表示上,当对象处于激活时期, 生命线可以拓宽为矩形, 这个矩形条成为激活条,有时也成为控制期。
消息(Message):消息允许在实体间传递信息 (传递参数), 允许实体请求其它服务, 对象之间通过发送和接收消息进行通信,消息可以触发操作, 唤起信号, 或使目标对象创建或销毁。消息又主要分为同步消息和异步消息。
同步消息(synchronous message):直接调用对象的方法, 执行方法返回结果, 这种具有返回控制机制的操作是同步通信,同步消息的发送者在继续其他动作之前,需要等待消息的响应。
异步消息(async message):与同步消息相对应的,异步消息发送者在继续其他动作之前,无需等待消息的响应。
约束:约束是一个条件,格式是: [Boolean Test]
组合片段:组合片段用来解决交互执行的条件及方式。它允许在序列图中直接表示逻辑组件,用于通过指定条件或子进程的应用区域,为任何生命线的任何部分定义特殊条件和子进程。常用的组合片段有抉择(Alt)、选项(Opt)、循环(Loop)、中断(Break)、并行(Par)、关键(Critical)、弱顺序(Seq)、强顺序(Strict)等,其中较常用的包括抉择(Alt)、选项(Opt)、循环(Loop)、并行(Par),我们对之进行阐述。
抉择(Alt):抉择用来指明在两个或更多的消息序列之间的互斥的选择,相当于经典的if..else..。抉择在任何场合下只发生一个序列。 可以在每个片段中设置一个临界来指示该片段可以运行的条件。else 的临界指示其他任何临界都不为 True 时应运行的片段。如果所有临界都为 False 并且没有 else,则不执行任何片段。
选项(Opt):包含一个可能发生或不发生的序列,相当于是一个可选项。
循环(Loop):片段反复一定次数。 能够在临界中指示片段反复的条件。Loop 组合片段具有“Min”和“Max”属性,它们指示片段可以重复的最小和最大次数。 默认值是无限制。
并行(Par):并行处理。 片段中的事件可以交错。
状态图(State chart Diagram)
状态机(state machine)是一种记录下给定时刻状态的设备,它可以根据各种不同的输入对每个给定的变化改变其状态或引发一个动作。比如:计算机操作系统中的进程调度和缓冲区调度都是一个状态机。
在UML中,状态机由对象的各个状态和连接这些状态的转换组成, 是展示状态与状态转换的图。在面向对象的软件系统中,一个对象无论多么简单或者多么复杂,都必然会经历一个从开始创建到最终消亡的完整过程,这个过程通常被称为对象的生命周期。一般来说,对象在其生命周期内是不可能完全孤立的,它必然会接受消息来改变自身,或者发送消息来影响其他对象。而状态机就是用于说明对象在其生命周期中响应时间所经历的状态序列以及其对这些事件的响应。在状态机的语境中,一个事件就是一次激发的产生,每个激发都可以触发一个状态转换。
状态机由状态、转换、事件、活动和动作五部分组成。
- 状态:状态指的是对象在其生命周期中的一种状况,处于某个特定状态中的对象必然会满足某些条件、执行某些动作或者是等待某些事件。一个状态的生命周期是一个有限的时间阶段。
- 转换:转换指的是两个不同状态之间的一种关系,表明对象在第一个状态中执行一定的动作,并且在满足某个特定条件下由某个事件触发进入第二个状态。
- 事件:事件指的是发生在时间和空间上的对状态机来讲有意义的那些事情。事件通常会引起状态的变迁,促使状态机从一种状态切换到另一种状态,如信号、对象的创建和销毁等。
- 活动:活动指的是状态机中进行的非原子操作。
- 动作:动作指的是状态机中可以执行的那些原子操作。所谓原子操作,指的是他们在运行的过程中不能被其他消息中断,必须一直执行下去,以至最终导致状态的变更或者返回一个值。
状态图:一个状态图(Statechart Diagram)本质上就是一个状态机,或者是状态机的特殊情况,它基本上是一个状态机中元素的一个投影,这也就意味着状态图包括状态机的所有特征。状态图描述了一个实体基于事件反应的动态行为,显示了该实体是如何根据当前所处的状态对不同的事件作出反应的。在UML中,状态图由表示状态的节点和表示状态之间转换的带箭头的直线组成。状态的转换由事件触发,状态和状态之间由转换箭头连接。每一个状态图都有一个初始状态(实心圆),用来表示状态机的开始。还有一个终止状态(半实心圆),用来表示状态机的终止。状态图主要由元素状态、转换、初始状态、终止状态和判定等组成。如下为一个较为典型的电话的状态图:
状态:状态用于对实体在其生命周期中的各种状况进行建模,一个实体总是在有限的一段时间内保持一个状态。状态由一个带圆角的矩形表示,状态的描绘要素应该包括名称、入口和出口动作、内部转换和嵌套状态。如下图,为一个简单状态:
转换:在UML的状态建模机制中,转换用带箭头的直线表示,一端连接源状态,箭头指向目标状态。转换还可以标注与此转换相关的选项,如事件、监护条件和动作等,如下图所示。注意:如果转换上没有标注触发转换的事件,则表示此转换自动进行。
初始状态:每个状态图都应该有一个初始状态,它代表状态图的起始位置。初始状态是一个伪状态(一个和普通状态有连接的假状态),对象不可能保持在初始状态,必须要有一个输出的无触发转换(没有事件触发器的转换)。通常初始状态上的转换是无监护条件的,并且初始状态只能作为转换的源,而不能作为转换的目标。在UML中,一个状态图只能有一个初始状态,用一个实心圆表示。
终止状态:终止状态是一个状态图的终点,一个状态图可以拥有一个或者多个终止状态。对象可以保持在终止状态,但是终止状态不可能有任何形式的和触发转换,它的目的就是为了激发封装状态上的转换过程的结束。因此,终止状态只能作为转换的目标而不能作为转换的源,在UML中,终止状态用一个含有实心圆的空心圆表示。
判定:活动图和状态图中都有需要根据给定条件进行判断,然后根据不同的判断结果进行不同转换的情况。实际就是工作流在此处按监护条件的取值发生分支,在UML中,判定用空心菱形表示。
一个简洁完整的状态图可以帮助一个设计者不遗漏任何事情,最大程度地避免程序中错误的发生,状态图的作用主要体现在以下方面
- 状态图清晰地描述了状态之间的转换顺序,通过状态的转换顺序也就可以清晰地看出事件的执行顺序。如果没有状态图我们就不可避免地要使用大量文字来描述外部事件的合法顺序。
- 清晰的事件顺序有利于程序员在开发程序时避免出现事件顺序错误的情况。例如,对于一个网上销售系统,在用户处于登录状态前是不允许购买商品的,这就需要程序员开发程序的过程中加以限制。
- 状态图清晰地描述了状态转换时所必需的触发事件、监护条件和动作等影响转换的因素,有利于程序员避免程序中非法事件的进入。例如,飞机起飞前半小时不允许售票,在状态图中就可以清晰地看到,可以提醒程序员不要遗漏这些限制条件。
- 状态图通过判定可以更好地描述工作流因为不同的条件发生的分支。例如,当一个班的人数少于10人的时候需要和其他班合为一班上课,大于10人则单独上课,在状态图中就可以很明确地表达出来。
活动图(Activity Diagram)
活动图是状态机的一个特殊例子,它强调计算过程中的顺序和并发步骤。活动图所有或多数状态都是活动状态或动作状态,所有或大部分的转换都由原状态中完成的活动触发。典型的一个活动图的例子如下所示:
活动图的含义:活动图是一种用于描述系统行为的模型视图,它可用来描述动作和动作导致对象状态改变的结果,而不用考虑引发状态改变的事件。通常,活动图记录单个操作或方法的逻辑、单个用例或商业过程的逻辑流程。
活动图的表示 :在UML中,活动图的起点用来描述活动图的开始状态,用黑的实心圆表示。活动图的中止点描述活动图的终止状态,用一个含有实心圆的空心圆表示。活动图中的活动既可以是手动执行的任务,也可以是自动执行的任务。
活动图与状态图的区别:
- 活动图可以算是状态图的一个变种,并且活动图的符号与状态图的符号非常相似,有时会让人混淆。
- 活动图的主要目的是描述动作及对象的改变结果,而状态图则是以状态的概念描述对象、子系统、系统在生命周期中的各种行为。
- 活动图中的状态转换不需要任何触发事件。活动图中的动作可以放在泳道中,而状态图则不可以。
活动图的作用:活动图是模型中的完整单元,表示一个程序或工作流,常用于计算流程和工作流程的建模。活动图着重描述用例实例或对象的活动,以及操作实现中所完成的工作。活动图通常出现在设计的前期,即在所有实现决定前出现,特别是在对象被指定执行所有活动前。一个活动图的示例如下图所示:
部署图(Deployment diagram)
概念: 部署图(deployment diagram,配置图)是用来显示系统中软件和硬件的物理架构。从部署图中,您可以了解到软件和硬件组件之间的物理关系以及处理节点的组件分布情况。使用部署图可以显示运行时系统的结构,同时还传达构成应用程序的硬件和软件元素的配置和部署方式。
作用:一个UML部署图描述了一个运行时的硬件结点,以及在这些结点上运行的软件构件的静态视图。部署图显示了系统的硬件,安装在硬件上的软件,以及用于连接异构机器之间的中间件。创建一个部署模型的目的包括:
- 描述系统投产的相关问题。
- 描述系统与生产环境中的其它系统间的依赖关系,这些系统可能是已经存在,或是将要引入的。
- 描述一个商业应用主要的部署结构。
- 设计一个嵌入系统的硬件和软件结构。
- 描述一个组织的硬件/网络基础结构。
包含元素:
节点:代表一个运行时计算机系统中的硬件资源。
- 节点的表示:在UML中,节点用一个立方体来表示。每一个节点都必须有一个区别于其他节点的名称。节点的名称是一个字符串,位于节点图标的内部。节点的名称有两种表示方法:简单名字和带路径的名字。简单名字就是一个文字串;带路径的名字指在简单名字前加上节点所属的包名。下面的立方体表示一个节点,其名称为Sun SPARC Server,并且制定了stereotype为device。
- 节点的类型:节点代表一个硬件设备或软件运行的环境,通常包括处理器(Processor)和设备(Device),处理器是能够执行软件、具有计算能力的节点,设备是没有计算能力的节点,通常情况下都是通过其接口为外部提供某种服务,例如打印机、IC读写器,如果我们的系统不考虑它们内部的芯片,就可以把它们看作设备。
- 节点中的构件:在UML1.x部署图规范中,组件可以直接部署到节点中。但是在UML2.x规范中,Artifact可以部署到节点,Artifact也可以实现组件。组件则可以通过Artifact间接部署到节点。
- 节点属性:像类一样,可以为一个节点提供属性描述,如,处理器速度、内存容量、网卡数量等属性。可以为节点其提供启动、关机等操作属性。
- 节点与构件:节点表示一个硬件部件,构件表示一个软件部件。两者有许多相同之处,例如二者都有名称,都可以参与依赖、泛化和关联关系,都可以被嵌套,都可以有实例,都可以参与交互。但它们之间也存在明显的区别:构件是软件系统执行的主体,而节点是执行构件的平台;构件是逻辑部件,而节点表示是物理部件,我们在物理部件上部署构件。
连接:部署图用连接表示各节点之间通信路径,连接用一条实线表示。对于企业的计算机系统硬件设备间的关系,我们通常关心的是节点之间是如何连接的,因此描述节点间的关系一般不使用名称,而是常常使用stereotype描述。
部署图的级别:部署图可以用于描述规范级别的架构,也可以描述实例级别的架构。这与类图和对象图有点类似。
- 规范级别(Specification level):展示从实体到节点的部署情况。不涉及具体的实体实例或节点实例。
- 实例级别(Instance level):展示实体实例到具体的节点实例的部署情况。它可以用于展示不同部署之间的差异。例如,开发和生产环境的部署可以通过具体的名字或ID(这里指的是具体的构建、部署服务器、或设备)加以区分。如下图中,应用程序部署到了应用程序服务器wsrv-01上,而对应的database schemas部署到了database server dbsrv-14上。
后记
UML本身是一套表示法,是一种工具,是用来进行面向程序分析和设计的工具,虽然其背后的OOAD的能力更加重要,但是工欲善其事必先利其器,一种良好的工具能对分析和设计能力有较大的促进作用,也可以让分析和设计工作更加的规范、专业和高效。需要说明的是,大部分的同仁可能更加重视UML在设计中的作用,实际上它也是用于面向对象分析的利器,对理清需求,进行分析有着重大的作用和意义。