UML活动图的JAVA 代码自动生成技术的实现

2022-06-07 12:51严海星李艳
福建技术师范学院学报 2022年2期
关键词:代码生成结点代码

严海星,李艳

(福建技术师范学院大数据与人工智能学院,福建福清 350300)

随着软件产业的快速发展,自动代码生成技术将成为未来软件开发的新方向,用户在完成软件设计后,可以通过一些自动代码生成工具将设计文件自动转换成代码,从而有效提升软件开发的效率,降低软件开发成本,提高软件开发的准确性.目前,自动代码生成技术主要有以下几种:基于模板的自动代码生成技术、基于模型的自动代码生成技术、基于对象关系映射的自动代码生成技术、基于文档注释的自动代码生成技术、基于动态代理类的自动代码生成技术[1].其中基于模板的自动代码生成技术需要元数据和模板文件,对语言框架依赖度较高;基于模型的自动代码生成技术,往往需要较强的理论支撑;基于对象关系映射的自动代码生成技术一般应用在特定领域,例如关系数据库的模型映射;基于文档注释的自动代码生成技术需要严格的文档注释,不具备通用性;基于动态代理类的自动代码生成技术适用于对象模型的代码生成.本文采用基于模型的自动代码生成技术将UML 活动图模型转换成JAVA代码(如图1 所示),将平台无关模型(PIM)采用转换工具转化成平台相关模型(PSM),再根据代码生成引擎生成JAVA 平台的代码,这种双层模型转化最大的优点是可以将软件的业务逻辑和实现细节完全分开,使软件开发人员可以把更多精力放到业务逻辑的分析与设计上.

图1 基于模型的自动代码生成技术流程图

1 UML 简介及UML 转代码相关研究

统一建模语言(UML)是一套面向对象设计的建模工具,通过4+1 视图(如图2 所示)可以从多方面展示一个完整的软件系统.逻辑视图用来描述软件内部的静态结构和动态行为,由类图、对象图、状态图、活动图、序列图和协作图组成.组件视图用来描述软件的组件以及它们之间的依赖关系,由组件图组成.用例视图是从外部用户的视角来描述软件的功能,由用例图组成.并发视图用来描述软件代码并发运行及系统环境中异步事件的处理,由协作图和活动图组成.配置视图用来描述系统物理设备的配置部署情况,由部署图组成.UML可以清晰地描述软件系统的业务逻辑,因此很适合用作自动代码生成技术的转化模板.

图2 UML 4+1 视图

目前已有许多专家学者对UML 模型进行自动代码生成的研究.文献[2]详细介绍了UML 类图的自动代码生成规则和策略,主要研究软件系统的静态部分;文献[3]详细介绍了UML活动图和序列图的自动代码生成规则,通过序列图和活动图之间的关联,对活动对象的交互过程进行代码转换,没有对单个活动图的行为逻辑代码转换进行研究;文献[4]对UML 类图和序列图自动生成C++代码提出了具体的转换方法,其中软件动态行为模型主要是多个对象间的交互模型,没有研究单个活动对象内转换过程;文献[5]详细介绍UML 类图和用例图自动生成基于MVC 的Web 应用的方法,侧重于使用模型数据快速生成基于MVC的Web 应用.文献[6]介绍了UJECTOR 工具将UML 类图、序列图和活动图自动生成JAVA代码的原理,侧重于活动对象间的交互行为转换,缺少对单一模型内的行为验证.事实上,UML 活动图描述了软件内部的工作流,研究工作流自动转换成指定平台的代码具有重要意义.本文对UML 活动图内部结构进行了深度的研究,分析了UML 活动图的信息提取方法以及自动转换成JAVA 代码的转换方法,同时针对单个活动图具有的行为不规范问题,提出了2 条验证规则,最后开发了自动转换工具Mdl2Java,可以将UML 活动图自动转换成JAVA 代码.

2 活动图信息提取

目前提取活动图信息的方法主要有两种,第一种通过分析UML 模型构造树,将UML 模型转换成XML 模型[7].第二种利用Rose 提供的基于COM 技术的REI 接口对UML 模型数据进行提取[8].本文使用Rational Rose 工具对UML 活动图建模,保存模型可以生成MDL 文件.在上述第一种提取方法的基础上,将UML模型保存在特定的数据结构中,便于后续分析模型信息以及对JAVA 代码的转换,同时,由于加载数据结构所占用的内存空间远小于加载XML 文件所占用的空间,且不需要XML 解析器,因此,可以在空间和时间上提升转换工具的性能.

MDL 文件的语法信息[9]如图3 所示,一份MDL 文件由对象Object、字面量Literal和对象列表List 组成,其中对象Object 记录活动图结点信息,对象列表List 由一系列对象Object 组成,字面量Literal 由字符串变量Value、元组Tuple、标号Tag、位置Location、字符串常量stringliteral、整数int 和布尔值boolean 组成.通过MDL 语法信息可知,提取活动图信息的方法,主要是解析MDL 文件,遍历MDL 文件中的对象列表List 获取每个对象Object,然后从对象Object 中筛选活动图中的结点和边的信息.通过对活动图对象的分析,记录活动图结点和边的字段主要是states、transitions 和statediagrams, 其 中states 记 录活动图的结点信息,包括结点类型、结点名称、结点ID 和是否是开始结点和结束结点,transitions 记录活动图中边的信息,包括边ID、边的名称、映射起点ID、映射终点ID、映射条件和发送事件等. statediagrams 记录结点和边的样式信息,包括坐标、大小、字体、颜色等.本文主要对活动图结点和边的信息进行提取,因此主要分析states 和transitions 字段.

图3 MDL 语法信息图

2.1 提取活动图结点信息

遍历states 列表内所有对象,将每个结点信息保存在数据结构HashMap 中,其中key 值保存结点ID,value 值保存结点类型和结点名称.结点类型有State、ActivityState、Decision 和SynchronizationState. State 表示状态结点,包括起始状态结点和终止状态结点.ActivityState 表示活动结点,用来记录每个活动信息.Decision表示分支结点,包括选择结点和融合结点.SynchronizationState 表示并发结点,包括并发分结点和并发合结点.

2.2 提取活动图边信息

遍历transitions 列表内所有对象,将每个对象的信息进行拼接后保存在数据结构ArrayList中,其中拼接字段包括映射起点ID、映射终点ID 和映射条件.由于边的名称和发送事件在转换成JAVA 代码时是无效信息,因此在拼接字段内没有保存这两个字段.通过查找结点ID,可以获得结点信息,通过映射起点ID 和映射终点ID 可以确定活动图唯一的有向边,通过映射条件可以确定执行当前活动边的前置条件,通常在映射起点是分支结点时才会有映射条件.

2.3 规则验证

由于活动图没有严格的数学验证,本文对活动图添加了约束规则,提取后的活动图信息要符合约束规则才能进行转换.约束规则如下.

规则1入度大于1 的结点一定是合并结点(分支合并或者并发合并) .

结点的出度是当前结点的后继结点的个数,结点的入度是当前结点的前驱结点个数.当结点的入度大于1 时,说明该结点有多个前驱结点,根据规则1,该结点一定是合并结点.活动图中的合并节点,包括分支结构中的融合结点和并发结构中的并发合结点.活动图信息提取后,会查找所有入度大于1 的结点,如果此结点的类型不是上述两类结点,则报错处理.

规则2活动图中除了起始结点外,不存在入度等于0 的结点.

活动图中除了起始结点的入度等于0 外,如果存在其他结点的入度等于0,那么活动图一定存在孤立结点或孤立子图.活动图的结点信息提取后,会查找所有入度等于0 的结点,如果存在这样的结点且不为起始结点,则报错处理.

3 活动图转JAVA 代码

由于活动图是有向图,方向代表程序执行的先后顺序,因此活动图转JAVA 代码时(如图4 所示),先按照每个结点的类型进行JAVA 代码转换,再使用代码整合器对代码进行整合.本文使用栈的数据结构来遍历活动图,栈顶元素表示待转换的结点,使用HashMap记录每个结点的剩余遍历次数,初始值等于结点的出度.结点每遍历一次,结点在HashMap中的值减1,当其值等于零时,表示当前结点已经完全遍历结束,结点出栈,后继结点入栈.开始转换时,先将起点入栈.起点转换完成后,起点在HashMap 中的值减1,此时起点的值等于零,起点出栈,起点的后继结点入栈.下面详细介绍每个结点的转换过程.

图4 代码转换流程图

3.1 状态结点转换器

状态结点代表程序的开始和结束,在代码转换时,不需要转换成JAVA 代码.然而,在转换程序开始前,需要进行开始结点和结束结点的检查,如果不存在开始结点和结束结点,转换程序将中止转换,并报错处理.

3.2 活动结点转换器

活动结点转换器把活动结点转换成具体的方法,活动名称转换成方法名称.由于活动图没有描述每个活动内的细节,因此活动结点转换成JAVA 代码后,只是一个空方法.活动结点只有一个后继结点,因此活动结点在HashMap 中的初始值都是一,在转换完成后,其在HashMap中的值为零,当前活动结点出栈,后继结点入栈.

3.3 分支结点转换器

分支结点转换器判断结点是选择结点还是融合结点.如果是选择结点,则会被转换成if 结构,同时查找以当前结点作为映射起点的所有边,将每条边的映射条件作为if 内的判定条件.映射终点以及后续结点都是if 内部语句,直到遇到融合结点才结束当前的if结构.此时,判断当前选择结点的剩余遍历次数是否等于零,如果不等于零,则将剩余遍历次数减1,同时转换没有遍历过的分支.如果剩余遍历次数等于零,表示整个分支结构完全遍历结束.分支结点出栈,融合结点的后继结点入栈.

3.4 并发结点转换器

并发结点转换器判断结点是并发分结点还是并发合结点.如果是并发分结点,则新建Thread 子类,并在子类的run 方法内调用所有后继结点转换后的代码,直到遇到并发合结点为止.每次遍历到并发合结点时,实例化刚刚定义的子类,并用子类调用start 方法.此时判断并发分结点的剩余遍历次数是否等于零,如果不等于零,则将并发分结点的剩余遍历次数减1,同时转换其他没有遍历的并发路径.如果剩余遍历次数等于零,表示所有并发路径都遍历结束.此时调用所有线程对象的join 方法,使转换后的代码确保所有的子线程都运行结束,才能继续执行主线程.

3.5 代码整合器

代码整合器的功能是将之前各个阶段转换的代码进行整合.整合时,为了便于代码阅读,所有的预定义部分(变量定义、方法定义、类定义)都放在了最前面,调用语句和实例化语句放在后面.代码整合完成后,将全部代码输出到JAVA 文件内.

4 支付子系统活动图实例

本节通过一个支付子系统转JAVA 代码的实例,来介绍具体情况.支付子系统活动图如图5 所示,用户调用支付子系统后,准备付款,首先进行身份验证,然后判断是否需要密码,如果需要密码则令用户输入密码,然后判断密码是否正确,输错密码会报错处理.如果密码输入正确或不需要密码,则扣除余额,同时系统并发执行以下操作:更新订单信息、通知商家和更新库存,所有操作都执行完成才结束程序.

支付子系统活动图包含状态结点、活动结点、分支结点和并发结点,提取活动图信息后,进行规则1 的约束验证,从图5 中可以看出,出度大于1 的结点有两个,一个是融合结点,另一个是并发合结点,均满足规则1 的约束.按照上述转换算法开发的Mdl2Java 工具对本活动图进行JAVA 代码转换,转换后的代码如图6 所示.

图5 支付子系统活动图

图6 支付子系统活动图的转换结果

5 结论

基于模型驱动的自动代码生成技术,可以将一些模型图自动生成指定平台的代码,进而将程序员从繁重的编码工作中解脱出来,使其可以将更多的精力放在软件模型设计上.本文使用的模型是UML 活动图,通过本文的算法,可以把符合规则约束的活动图自动转成JAVA 代码,从而加快软件开发的进度.最后,根据本文的转换算法,开发了自动转换工具Mdl2Java,实现了全自动的代码转换,具有较高的应用价值.

猜你喜欢
代码生成结点代码
LEACH 算法应用于矿井无线通信的路由算法研究
基于八数码问题的搜索算法的研究
Lustre语言可信代码生成器研究进展
一种基于模型和模板融合的自动代码生成方法
创世代码
创世代码
创世代码
创世代码
基于Web应用界面的代码自动生成软件设计
代码生成技术在软件开发中的应用