统一建模语言(UML) 版本 2.0对模型驱动开发的支持 |
级别: 初级 Bran Selic (mailto:[email protected]?subject=统一建模语言(UML) 版本 2.0), 杰出工程师, IBM 2005 年 4 月 29 日 所谓的“模型驱动”开发(MDD)方式,已经显示出了它们从根本性上提高软件质量和开发生产力方面的潜力。与传统的方法相比,这种方式是基于较高层次上的抽象和更好的自动化利用的。由于建模语言对MDD的成功具有关键性的作用,所以最近完成了对基于工业标准的统一建模语言(UML)的主要修订。随着一些重要的新的建模能力添加到其中――比如更精确地获得软件架构的能力――这次修订的主要特性使得语言定义更加精确,从而达到了更高层次的自动化。这篇文章解释了这一特性是如何实现的,并且描述了 UML 2.0 的其他亮点。
可以看到1990年的早期版本已经对对象模式和相关技术有着浓厚的兴趣。基于这个模式的新的编程语言(比如Smalltalk, Eiffel, C++, 和Java)已经被设计并投入使用。伴随着这些语言出现的还有令人惊叹和难以理解的面向对象(object-oriented(OO))软件设计方法和建模符号。因而在彻底的纵览了有关OO分析和设计方法后(包含800页以上),Graham列举了50种以上的有相当大影响力的方法(Graham01)。考虑到对象模式包含了基本概念的相对较小的子集(包括封装、继承和多态),很明显,在这些方法中存在非常多的重叠和概念上的结合--大多数情况下是由于符号性的和其他并不重要的差异而使其变得很模糊不好理解。这样就导致了令人难以理解以及不必要的市场分歧—反过来也阻碍了有实用价值的新模式的采用。软件开发者很难在这些相互矛盾的语言,工具,方法和供应商中做出选择。 由于这个原因,当Rational 软件随后提出了统一建模语言(UML)的初始版时--在Grady Booch,Ivar Jacobson和Jim Rumbaugh的领导下--得到了快速和积极的反响。其目的并不是为了提出任何新的内容,而是—--通过高级领域思想领导者们的协作—--把各种各样的OO方法的最好特性添加到一个单独的和与供应商无关的模型化语言和注释中。正因为如此,UML很快地成为了一个广泛的实践标准。随着1996年对象管理组织(Object Management Group)对它的采用,UML成为了一个广泛被接受的工业标准[OMG03a] [OMG04] [RJB05]。 从那以后,UML:
在处理复杂软件时,UML同样能够帮助增强对模建模价值的普遍的认识。尽管这种非常实用的技术几乎和软件本身一样历史悠久—――像很早以前的例子那样它们都带有数据流图(flowcharts)和有限状态机—――大多数开发人员慢慢地才接受它,如同接受其他工具一样,而不是像接受一个有用的小工具那样迅速。客观的说,这种对新事物的态度仍旧是一个占有主导性地位的,这就是为什么模型驱动方法在这一领域中受到很大的阻力的原因。 之所以会存在上述情形是有一些原因的(当然也有些不是那么有根据的原因,比如:通常人们并不相信创新)。主要原因是软件模式经常会导致不可预知的严重性错误:我们都清楚,任何一个模式的实际价值与它的正确性直接成比例。如果一个模式不能把它所表示的软件系统向你准确的表现出来,那么还不如不用模式,因为它可能会导致错误的结论。那么提高软件价值的关键在于缩小这些模式和它们所模式化的系统之间的差距。然而,正如这篇文章后面所论述的,在软件中减小这种差距比在其他任何的工程学科中要容易的多。 某些错误的软件模式归咎于当前编程语言的过多的细节和敏感的本质。一些较小的失误和几乎不被发觉的编码错误――比如指针偏差或者变量未初始化――可能会带来严重的后果。举一个实例来说,在一个有相关文档记录的案例中记载,由于一个嵌套的
解决这一难题的方法是通过一个或多个自动的模型转换器将一个模型与它相应的软件实现从形式上连接起来。也许这方面最好和最成功的例子就是编译器,它能够将一个高级语言程序解释成一个与之相当的机器语言的执行程序。这种情况下,模式就是这个高级语言程序,它就像所有有用的模式那样,隐藏了潜在的计算技术特性上的相关细节(比如内存字符大小,累加器的个数,索引寄存器,ALU算术逻辑单元类型等等)。 有趣的是,几乎没有任何其他的工程媒介能够为一个模型和它相应的工程工具提供如此紧密的连接。这是因为你所模式化的工具是软件而不是硬件。任何一种物理工具的模式(比如:一辆汽车,一座建筑物,一架桥等等)不可避免地包含了一些步骤,它会将物理特性抽象成一个相应的模型(就像数学或几何模型一样)。同样,使用物理原料实现一个抽象的模型包含了从抽象到具体的非正式转换。这一非正式步骤的本质会导致一些错误,正如上面所提到的,会导致模型效率低下或是甚至达不到预期目的。然而,对软件来说,原则上,这种转换可以从各个角度的形式上的执行。 在抽象性和自动化抽象性与自动化操作强有力的结合后,所产生的潜能已经导致新的建模技术和相关发展方法的出现,正如所提及的模型驱动开发MDD) [Brown04] [Booch04]。MDD的定义特征是,此模型已经成为软件设计的主要工具,它把许多注意力从相关的程序代码上转移开。它们为不同的自动化和半自动化的方法提供服务,这种方法源于代码和相关的模型。与传统的编码相比较,目前在MDD中使用自动化操作的程度不同于从简单框架代码到完全自动的产生代码。很明显地,自动化程度越高,模型越精确,MDD的优越性就更突出。 软件发展的模型驱动方法不是一种独特地新式方法,在过去就已经被使用并获得了不同程度地成功。他们之所以愈来愈受重视的原因是,其支持性技术已经愈来愈成熟,成熟点在于比起过去的情况来看在实践上更加自动化。这不仅仅在效率方面,而且在可测量性方面,同样这种工具所具有的能力是与继承性工具和方法相结合的。这种成熟技术所反应的MDD标准的出现,使得使用相关的工具时更加便利舒适,给使用者带来显而易见的好处。其标准之一是统一建模语言的修订版。
UML 2.0是UML标准最主要的修订本,以下的是一系列次要的修订本[OMG04] [RJB05]。为什么修订UML是必要的呢? 修订这种语言最初的动机源于更好的支持MDD工具和方法的要求。在过去的十年中,许多供应商已经发展了基于UML的工具,值得注意的是这些工具所支持的自动化标准比传统的CASE(计算机辅助软件工程)工具标准更高。 为了支持这些更高标准的自动化形式,需要用一个比原始标准规定更加准确的方式来定义UML。(从与时俱进的角度来看,最初地原始UML标准设计是作为一种辅助工具来服务的,即为非正式的捕捉和设计意图的传达提供服务)。不幸地是,这些定义因商家的不同而不同,它的危险性导致了一些分歧的产生,而这些分歧是应该从旧版本的标准中被排除掉的。一个新版本的标准可以修正它。 另外,在近十年的使用UML的实践经验之后――同样在此期间重要的新技术也随之产生了(例如基于web的应用软件和基于服务的体系结构),新的建模性能得到了肯定。事实上,当这些新技术通过现有UML概念的适当结合表现出来时,将它们作为优秀的内嵌语言特性引入是有明显益处的。 最终,在同样漫长的时间内,业界已经学会了许多有关如何使用的适当方法来构建和定义模型语言。例如,目前将要出现的外部模型和模型转换的理论,它强制性要求一个模型语言如何来定义。由于与当前程序设计语言理论相比,我们一直缺乏一个统一的、系统的建模语言设计理论,因此我们需要把这些理论以及类似的发展合并在UML中,这样才能确保其效用和持久性。
UML 2.0的新改进可分为以下五个主要方面,按重要性顺序列出:
现在我们来更详细地研究一下上述的每个方面。 大多数的早期软件建模语言被非正式地定义,并很少注重它的精确性。时常,建模概念被解释成使用不严密的自然语言。由于大多数的建模语言在文件中或在Martin Fowler所提及的设计草图[Fowler04]中所使用,在那个时期,此模型概念得到了充分信任。这种思想传达了一种设计的本质特性,而把细节留给实现阶段去处理。 然而,由于模型在这种语言中很可能――并且通常是――被不同的商家解释成不同的含义,因此经常导致概念混淆。此外,除非模型解释的问题事先已被明确地讨论过,否则像这样的分歧还不能被人所发觉,而只是在发展的较后阶段才能被发现(即当问题的结果已明显显现时候)。 为了把不明确的概念减少到最少――并和多数现代的其它模型语言形成对比―― 第一个标准化的UML定义是用元模型来指定的。这是一个定义每一种UML 建模概念特性和这些特性与其他相关概念直接的关系的模型。使用UML的基本子集来定义这个元模型,并且通过一系列在对象约束语言中(OCL)正式的强制进行补充。 注释:: 这种UML子集,主要是由定义在UML上的类图上的概念所组成的,它被称为元对象工具(MOF)。选择这个子集后,它可以用来定义其它的建模语言。 这种结合所描述的是,UML抽象语法的一种正式的规范。正式的规范。之所以被称作是正式的,其原因是它与实际的符号或用于描绘模型的具体语法具体语法(也就是说,文本和图表)无关。换句话说就是,它所定义的规则集可以用来确定一个特定的模型是否已经很好的成形很好的成形。例如,这种规则将允许我们去测定通过一个状态机转换来连接两个UML类是不正确的。 然而,在这个初始的UML 元模型中所使用的精确程度证实,在MDD(例如在[Stevens02]中讨论所见到的)后,对整个潜能的支持是远远不够的。特别是,UML建模概念的语义(或含义)的规范,对这些作为自动代码生成或正式确认的基于MDD的活动仍旧是不适当的。 因此,值得注意的是在UML 2.0中定义所使用的精确程度已经增强了。它是通过以下方法完成的:
图 1. UML2.0语义框架 UML 2.0 在精确度方面的提升所造成的最直接的结果之一,就是即使不算它所新增的建模能力部分,这种语言的定义也变得更大了。特别是对于最初的UML,曾被批评过于庞大(以至于学习和使用起来太麻烦),对于现在更加庞大的定义,这点通常又将被关注。 这样的批评典型地忽略了一个事实,那就是UML原本就是用来表述一些现在最复杂的软件问题,这样的问题当然需要功能充分强大的工具。(成功的科技——如同汽车和电子学,从来没有变得简单;对机器持续不断的要求是人类的本性之一,这就造成了最终越来越复杂的工具。例如,没有人会企图用基本的手工建造现代的摩天大厦。) 不过,由于有了这些顾虑,UML2.0 在某种程度上进行了模块化,允许有选择性的使用一些语言模块,以便解决语言复杂度的问题。这种结构的通常形式如图2所示。一些像类和关联这样的共享概念组成了它的基础部分,顶部是垂直的子语言或语言单元的一个集合,集合中的每个单元都很适合用来对某个具体的方面进行建模(Table 1)。这些垂直的语言单元一般都是相互独立的,因此你可以单独地使用它们。(注意:这在UML1中是不行的,在UML1中,活动的形式完全是基于状态机的形式。) 图 2. UML 2.0的语言架构 此外,垂直语言单元按级别组织成三层,通过在那些可用的层上增加建模能力可以形成相互连续的更高层。这就对模块性提供了更多的空间,即使对一个已给定的语言单元,你也有可能只使用某些特定的子集。 这样的架构意味着,你可以学习和使用UML那些最适合你的部分。你不再需要为了有效地使用UML去熟悉它所有的内容,就如同你不必为了说好英语而去学习英语里所有的内容一样,从这点来说,它可能比学英语更简单。随着你经验的增长,如果有必要你可以逐渐引入更强大的建模概念。
作为相同架构下重组的一部分,在UML2.0中,语言的定义和结构的灵活性被显著地简化了。在UML1中,规范性的基本单元是由元模型的包定义的,包含了差不多成百个可能的组合。(事实上,因为UML 1 为一个特定的适应性给出了规范化的但又不完全的灵活定义 ,也就是说这些性能可以有很多种不同的组合)这就意味着,几乎不可能找到两个或更多的建模工具能相互之间进行模型交换,因为每一种工具可能只支持包的一种不同的组合。 在UML 2.0中,只定义了三个规范性层次,那些对应于分级语言单元的层已经在0层中就被提及并描述了。它们是这样被定义的,层(n)的模型服从于任何比它更高的层(如n+1)所定义的模型。换句话说,一个符合给定层规范的工具可以从那些符合任一相同或低于它所在层规范的工具中导入模型――在没有丢失信息的情况下。 注释: 形式上,UML 2 也定义了第四层(层 0),但这只是一个内部层,主要用来供工具的实现者使用。 四种规范标准类型的定义
这就意味着最多有12种不同规范标准组合,并且它们之间有着清晰的从属关系。(比如,抽象和具体的语法标准与仅仅是具体的或仅仅是抽象的语法标准保持一致)。从而使得在UML2.0中不同厂商的工具之间的模型交换成为可能,而不仅仅只停留在理论上。 在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举例说明了一个扩展的交互模型。在这个例子中,交互活动 在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的概念。这种限定是通过定义上的限制来限制那些给它们提供一种唯一的特殊领域解释的概念。例如,一个限制可能不允许多重继承,或者是它可能需要一个类必须具备一个特殊的属性类型。这就意味着:
因此,大多数的防止差异上的分裂问题可以完全地减轻或是甚至避免。这种推理方式带来了国际化标准,是形成SDL【ITU02】的原因――这个SDL广泛地在电信上使用――从而重新将SDL定义为一个UML Profile 。【ITU00】【ITU03】。 这并不是说任何一个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工具和专门技术上有着独特的优势。 所有这些导致了第二代建模语言的产生,这种语言将使你更快更可靠的开发成熟的软件系统――同时允许你继续使用相同类型的经验和知识,也是每个软件开发人员所要掌握的技能。 |