在UML2.0中,新增的特性相对来说不是很多。这是特意为了避免已经声名狼藉的“次要系统”效应[Brooks95],因为一门语言没有必要由于非常多变的分用户群的提出新的需求而过度的膨胀。实际上,新的建模能力的主要实质是对已存在的特征进行简单地扩充,以便用于大规模的软件系统的建模。
此外,这些扩展都是通过使用相同的基本方法达到的:即在不同的抽象层面上递归地应用那些相同的基本概念集。这就意味着,你可以把一个给定类型的模型元素合并到单元里,依次类推,你可以用这种方式在下个抽象层面上进行合并,并把这些合并后的单元作为一个模块进行使用。这跟编程语言中的过程类似,它能根据你想要的深度进行嵌套的调用。
特别是,以下建模能力通过这样的方式被扩展了:
复杂结构
活动
交互
状态机
上述的前三项占到了UML2.0的新特性中的90%以上
复杂结构
这一系列特征的依据来自于对不同架构描述语言(如UML-RT [SR98], Acme [GMW97], 和 SDL [ITU02])长期使用的经验。这些语言的特点在于,它们通过相对简单的像图这样的概念被描述:基本的结构性节点,也就是所谓的部件,他们可以有一个或多个端口,它们之间可以由被称作连接器的通信通道进行连接(如图3所示)。这些集合体可以被封装成更高层的单元,依次类推,这些新封装成的单元也可以有自己的端口以便于与其他更高层的单元合并成更高层的单元
图 3. 复杂结构建模概念
从某种程度上来说,这些概念在UML 1 中对于协作的定义里可以找到,只可惜它们不能用于递归。为了允许递归,协作结构被嵌套到类的规范中。这就是说这个类的所有实例都将有一个由类定义的内部结构。例如,在图3中,部件(part)/a:A 和/b:B 都被嵌套在部件(part)/c:C中,从而表现了这个复杂结构类C的一个实例。而这个类的其他实例都会有相同的结构模式(包括所有的端口,部件以及连接器)
这就证明了,你可以通过这三个简单的概念,以及它们的递归应用,对任何复杂的软件架构建模。
活动
在UML中,活动被用来对不同种类的流程建模:信号流或数据流,也有算法流或过程流。不用说,对于众多的领域及应用而言,基于流的描述是最自然的表现方式了。对于业务过程建模者和那些想主要通过信号处理器浏览整个系统的系统工程师,这种形式更是特别受欢迎。不幸的是,在UML 1中,行为建模在流的类型方面有大量严格的限制,这些限制被提出了异议。这其中的很多限制都是由于在基本的状态机的顶部行为被覆盖了,所以,它们受限于状态机的语义。
UML 2.0中用一个消除了这些限制的更泛化的语义基础替代了状态机的底层。此外,这些语义基础也从很多行业标准和业务过程形式中得到灵感,其中包括BPEL4WS [BPEL03]——在基础的形式上增加了一系列非常丰富并且非常精确的建模特征。这包括以下一些能力:
中断的活动流
复杂形式的并发控制
多样的缓冲配置
这就形成了一个丰富的建模工具集,能广泛地表示不同的流类型。
由于其复杂的结构,你可以对活动递归地进行分组并对流进行连接以形成更高的层,这种层次清晰地定义了输入和输出。你可以一次把这些活动与其他活动合并形成更复杂的活动,一直到最高的系统层。
交互
在UML1 中,交互性是由协作图中序列消息的注释或单独的序列图来表现的。不幸的是,这样导致失去了两个基本能力:
对序列进行重用的能力,也就是可以在更广范围(或更高层)的上下文序列中重复的能力。比如,在一个应用程序中,一个验证密码的序列很可能出现在多个上下文环境中。如果不能对这些重复的序列进行打包形成单独的单元,你就得对它们进行多次的定义。这不仅需要在系统操作上增加,还使模型的维护更为复杂了。(例如,当序列需要修改时)
对不同的复杂控制流充分建模的能力,在表现复杂系统的交互性方面很普遍。这包括序列的重复,执行路径的选择,并发和顺序——独立执行等。
幸运的是,关于复杂的交互性问题在电信领域得到了广泛地研究。在多年定义通信协议的时间过程中,形成了一个标准。这个标准被作为主要依据用在UML2.0的交互性描述上。
关键的创新就是把交互性作为单独命名的建模单元进行引入。这样的交互性表现在内部对象间任意复杂的通信。它甚至可以被参数化以用来描述上下文独立的交互模式。
你也可以从更高层递归地调用这些打包了的交互活动,类似于宏调用(图4)。就像你所希望的那样,你可以在任意程度上去进行嵌套。此外,交互活动还能在诸如循环和选择这样的复杂控制的构造中提供操作数(例如,某个给定的交互活动可能需要重复某个具体的次数)。UML 2.0 定义了大量的这种类型的建模构造体,给你在分解后的任何层面上进行复杂的端对端建模,提供了非常大的便利。
图4. 一个复杂交互模型的例子
图4举例说明了一个扩展的交互模型。在这个例子中,交互活动ATM的访问首先调用另一个低层的叫CheckPIN的处理过程(整个交互活动的内容没有在图中显示),注意到后一个交互活动有一个参数(这个例子中也就是,在处理被取消前,一个无效PIN能被输入的次数)。之后,客户端发送一个同步的消息说明需要哪种交互活动,基于这个具体的值——DispenseCash活动或PayBill活动,将被执行。
在UML 2.0中,交互性不仅由序列图来呈现(如同上面的例子所展示),也通过其他类型的图(包括在UML1中定义的基于协作的形式)来表现。甚至还有通过非图形化的表格来体现。
状态机
添加到UML2.0中的主要新特性与之前的案例非常相似。基本思想是它可以创建一个复合状态的完整模块,它具有清晰的转换入口点和出口点。反过来,你也可以通过一个离散的和可重复使用的状态机的规范来分别地定义上述的复合状态的内在分解。也就是说,在这个状态机或某些其它的状态机中,相同的规范可以在多处重复使用。这样使得在不同上下文中的共享行为模式的规范更加简单化。
在UML2.0中另一个著名的状态机创新是在一个类与它的子类中继承的状态机的分类。
语言特殊化能力
UML1的实践表明了应用UML的一个相当通用的方式是,首先为一个特定的问题或领域定义一个UML Profile ,然后用这个 Profile 代替普通的UML。实质上,这些 Profile 就是一种生成像特定领域语言 (DSL)的方法。
使用UML Profile 的一种可选择的办法是使用MOF标准和工具定义一种新的自定义模型化语言。后者的方法显然有很大的优势,它具有语言定义功能,这种定义可以以非常好的的方式解决手头的问题。乍看上去,这种方式似乎是DSL定义的首选方式,但是进一步的观察会发现这种方法存在严重的缺陷。
如简介中所提到的,过多的差异会导致分裂性问题的出现,而设计UML的目的正是要消除这类问题的。
幸运的是,Profile 机制在这里为许多实际的案例提供了一个便利的解决方案。这是因为在不同的DSL之间存在相当多典型的公共部分。例如,实际上任何一个面向对象的建模语言都需要定义类,属性,关联,相互作用等概念。UML,作为一种多用途的建模语言,正好提供了这个便利和有用概念集合的谨慎定义。对多数可能的DSL来说这正是一个好的起点。
但是在这里不仅仅只是概念上的复用,因为从定义的角度,一个UML Profile 必须与标准UML保持一致。换句话说,一个UML Profile 限定了标准UML的概念。这种限定是通过定义上的限制来限制那些给它们提供一种唯一的特殊领域解释的概念。例如,一个限制可能不允许多重继承,或者是它可能需要一个类必须具备一个特殊的属性类型。这就意味着:
任何一种支持标准UML的工具可以基于上述的 Profile 来操纵模型。
任何有关标准UML的知识和实际经验都可以之间应用。
因此,大多数的防止差异上的分裂问题可以完全地减轻或是甚至避免。这种推理方式带来了国际化标准,是形成SDL=的原因――这个SDL广泛地在电信上使用――从而重新将SDL定义为一个UML Profile 。
这并不是说任何一个DSL都能并需要被实现成一个UML Profile ;确实存在很多的案例说明UML可能缺少必备的可以被转换到相应DSL概念中的基础性概念。尽管如此,UML通用性可能比许多人所想象的更广泛。
基于上述考虑,在UML2.0中的 profiling 机制已经被合理化并且它的性能也已经被扩展了。在原型和UML概念之间的连接已经被扩展了。事实上,一个UML2.0原型被定义成好像它仅仅只是一个现有UML元类的子集,并带有关联的属性(代表加有标签的值的标签),操作和限制。这个机制使用比如像OCL这样的语言描述那些限制,它已经被充分的说明了。
除了限制个别的建模概念以外,一个UML2.0框架同样可以明确的隐藏UML概念,从而使得在一个给定的DSL中没有任何意义和必要
最后,UML2.0 profiling 机制同时也可以用作一种机制,它可以从多种不同的域中观察到一个复杂的UML模型――从特定的角度――通常不一定具有DSL。也就是说,任何一个profile 都可以有选择性的以任何方式被应用或是不被应用,只要不影响基础的UML模型。例如,一个性能工程师可能会选择在模型之上通过将多种与性能相关的方法与模型元素连接,来应用一种性能模型化解释。然后可以通过一个自动化技术性能分析工具来决定。
一般性的合并
这一项特性适用于很多的领域,包括重复概念的消除和多数编辑上的修改(比如:给模糊的描述和标准化的术语以及特殊的格式添加相应的说明)
消除了重复和对缺乏定义的概念的说明也是UML2.0另一个重要的需求。受这个需求影响的主要有下面三个主要领域:
动作和活动
模板
基于组件的设计概念
在UML1.5中介绍过动作。动作的概念上的模型被特意的普通化,从而提供数据流和控制流计算模型。这就导致了与活动模型在概念上非常相似。UML2.0利用了这种相似,它为动作和活动提供了一个通用的在语法上和语义上的基础。从你的角度来看,在不同层次上的抽象显得有些过于的形式主义,因为它很典型模拟了不同层次的之间存在的现象。尽管如此,共享概念上的基础使得它完全的简单化和更加的清晰。
在UML 1中,定义模板是非常普遍的:任何的UML 概念都可能产生一个模板。不幸的是,这种普遍性是它应用上的一种阻碍,因为允许存在潜在的无意义的模板类型和可代替的模板。在UML 2.0中的模板机制受到一些容易理解的案例的限制如:分类器,操作,和包。前两种是在流行的程序语言创建模板机制后被模式化的。
基于组件的设计领域中,UML1 有很多使人混淆的概念。你可以使用类,组件,或子系统。这些概念除了在不同方法中有一些微妙的差别外,它们都有着许多共同点。关于在任意的特定情况下来使用,这里没有清楚的描绘。一个子系统仅仅是一个“较大的”组件吗?假如这样的话,毫无疑问的是它在成为一个子系统以前,一个组件要多大呢?类提供了封装和接口的实现,组件和子系统也可以做到。
在UML 2.0中,所有的这些概念都被结合在一起,所以组件被作为一个特殊的例子简单地定义了,即一个结构化类的更加全面的概念;同样地,子系统也仅仅是组件概念的一个特殊例子。两者之间从性质上的不同有清楚的定义,因此,当你使用基于客观的标准概念时,你便能果断地作出决定。
在编辑方面,规范的格式已经统一化了,使用了语法与简单参考资料相结合的模型概念符号规范。每一个元类规范被扩大化了,它明确地确定了语义的不同点,符号的选择权,以及它与UML 1规范的关系。最终,专业术语得到了统一,因此一个特定的条件(例如,类型,实例,规范,或事件)在它出现过的所有上下文中有着相同的全面含义。
总结
UML 2.0对驱动模型方法做了初步的介绍。那些喜欢将它作为一种绘图工具(正如在文章中先前所描述的那样)的使用者同样也可以像UML 1一样以非正式的方式使用它。此外,尽管新的建模性能是非插入式的,但是在大多数的案例中,这样的使用者将在语言的视觉和感觉上看不到任何变化。
然而,现在MDD阶段性进步的机会在标准化的方法中是可得到的。UML 2.0包含必要的精确性的增强,并且如果你希望你可以使用它的新特性的话――所有的方法都可以完全自动的产生代码。
语言的结构被谨慎的重新编制后允许采用一个模型和渐变的方法:你仅仅需要学习你所感兴趣的那部分语言,其余的你可以完全的忽视。随着经验和知识的增加,你能选择性地添加新的能力。随着重组带来了规范标准定义的极大的简化,它将促进了工具之间的互用性,同时也将促进来自不同商家之间的工具的互用性。
仅有少量新特性被添加里面(用来避免语言冗余),实际上所有这些都遵循相同的递规法则设计原则,从而使你能够模型化既大又复杂的系统。尤其是,这些扩展被添加以促进更加直接地建模软件体系结构,复杂的系统交互,和基于流程的建模,使它在例如商业过程建模和系统工程中被理想化的应用,。
语言扩展机制将被结构重组和简化,以给你提供一种更加直接地方法基于UML来定义特定领域语言(DSL),这些语言在直接的利用丰富的UML工具和专门技术上有着独特的优势。
所有这些导致了第二代建模语言的产生,这种语言将使你更快更可靠的开发成熟的软件系统――同时允许你继续使用相同类型的经验和知识,也是每个软件开发人员所要掌握的技能。