级别: 中级 Sandeep Kohli, 高级软件工程师, IBM Sreerupa Sen, 高级软件工程师, IBM
2007 年 8 月 13 日
学习如何使用 IBM® Rational® Software Architect 和 Rational® Systems Developer 工具将 UML 建模适于开发 C++ 应用程序。本文描述了7.0版本支持 C++ 领域建模的特性。在此,假设您正在不断更新模型和代码,并且想要实现 UML 与 C++ 之间的对应修改。那么您只需要拥有 UML 的基本知识,了解本文中介绍到的 Rational 软件产品,掌握如何运行转换以及如何应用概要文件和构造类型,就可以达到您的目的。
通过本文的姊妹篇 “使用 Rational Systems Developer 和 Rational Software Architect 进行 C++ 转换的提示与技巧,第 1 部分:控制由 UML 模型生成的 C++ 代码” 了解从 UML 模型生成 C++ 代码的方法与技巧。
条件及过程概述
本文解释了作为一名 C++ 开发人员,如何在使用统一建模语言 UML 建模的时候,继续使用 C++ 数据结构进行设计。您只需掌握 UML、IBM® Rational® Software Architect 和 IBM® Rational® Systems Developer 的相关基本知识即可。
- 在 IBM Rational Systems Developer 创建一个简单的 UML 模型。
- 将一个 C++ 概要文件应用在模型上,这样,就可以在建模的过程中使用 C++ 的数据结构。
- 将 C++ 类型库导入模型,这样,就可以使用 C++ 基本类型。
- 生成代码。
- 在类中添加方法体。
- 另外提供一些小技巧,以便在重复进行转换时保存代码和模型的修改。
创建一个简单的 UML 模型
UML 概要文件是扩展 UML 的一个标准机制,所以对某些特定领域的建模可以在不增加 UML 负担的情况下进行。UML 的创始人不可能涉足到所有的应用领域,因而,他们明智地提供了扩展机制。
应用 C++ 概要文件
例如,要对某个 C++ 具体的元素建模,比如 struct、union、typedef 等,您需要在 UML 模型上应用 C++ 概要文件。步骤如下:
- 确定您正处于 Modeling 透视图。
- 选中模型,然后单击 Properties 视图。
- 在 Properties view 左侧,从列表中选择 Profiles。
- 单击 Add Profile,在 Select Profile 对话框中 Deployed Profile 下方选择 C++ Transformation (如图1和2所示)。
图1. 应用 C++(CPP)概要文件
图2. 选择 C++ 概要文件
导入 C++ 类型库
UML 提供了一组预定义的类型,有 Boolean、Integer、String 和 UnlimitedNatural。而大多数的编程语言,包括 C++,提供了更为丰富的基本类型。在 C++ 中建模的时候,您可能经常需要使用 C++ 预定义的基本类型(比如,将一个类型赋给某个属性、参数、操作返回类型,等)。下面讲述如何导入 C++ 模型库:
- 在 Project Explorer 中右键单击 UML 模型。
- 如图3所示,选择 Import Model Library。
图3. 导入 C++ 类型库
- 如图4所示,在 Import Model Library 的运行界面上,从下拉列表框中选择 C++ Types。
图4. 选择要导入的 C++ 类型库
现在已经作好为 C++ 特定元素建模的准备了。请接着参看后面的示例。
创建一个简单的 UML 模型,car factory。
创建如图5所示的 UML 模型。创建的过程如下所示:
- 这个示例模型中,Bus 和 Car 的基类是 Vehicle。
-
Bus、Car 和 Vehicle 类被创建在一个名为 Vehicles 的 UML 包中。
- 同样的,在名为 Parts 的包中,建立Engine 和 Wheels 类。
-
Car 与 Engine 之间存在组合的关系。
- 理论上, Car 和 Engine 是不可分的,因为汽车需要引擎。
-
Car 和 Bus 与 Wheels 都有聚合的关系。
- 本例中,轮胎可以没有汽车而存在,汽车可以没有轮胎而存在 -- 至少在组装的时候。
图5.一个简单的汽车工厂 UML 模型的示例
在模型中创建 C++ 元素
- 接下来,创建一个名为
Strategy 的 UML 包。
- 在
Strategy,定义公共汽车的路线和起点。
- 创建一个名为
Route 的类,用来表示路线;一个名为 Address 的类,用来表示地址,比如公共汽车路线的起点。
-
Address 需要是 C++ struct 元素而不是普通的类。
- 为 UML 模型指定每个公共汽车的路线。如图6所示,您可以为
Vehicles 包中的 Bus 类和 Route 类之间设置聚合关系。
- 起始点是路线的一个属性。所以,为
Route 类增加一个类型为 Address,名称为 startingPoint 的属性。
图6. 名为 Strategy UML 包的内容
在图6中,请注意 Address 是 <<cpp_struct>>类。创建的步骤如下:
- 创建一个 UML 类,命名为
Address。
- 将
cpp_struct 原型应用给新建立的类。
- 将一个原型应用给 UML 元素,转到 Properties 视图。
- 如图7所示,从左侧的列表中选择 Stereotypes,然后单击 Apply Stereotypes。
图7. 应用一个原型
- 从该元素可用的原型列表中选择 cpp_struct。同时,也可以留意一下其它的选择,以备日后参考。
您很快就能发现,将这个模型转换成代码的时候,带有<<cpp_struct>>原型的 Address 类将会生成一个 struct 元素,而不是一个类。
配置转换并生成代码
在由模型生成 C++ 代码之前,需要通过创建转换配置来设置相应的参数。
创建转换配置
步骤如下:
- 单击 File > New > Others,然后选择 Transformations 文件夹中的 transformation configuration。
- 在 New Transform Configuration 向导中,指定配置文件名,此处命名为
tc1。
- 选择 transformation type 和 project。
- 展开 IBM Rational Transformations 列表,然后选择转换类型 UML to C++。
- 本练习中,使用已有 UML 模型项目来保存转换配置文件。
- 单击 Next,继续进行后面的步骤。
- 在 Source and Target 选项卡中:
- Source:选择 UML 模型。
- Target:创建一个新的 C++ 项目。
- 请确定 source 和 target 都是选中的,然后单击 Finish。
注:
如果需要,您可以再对转换配置进行修改。
生成代码
之后的步骤就比较简单了。
- 进行到现在,您已经完成了大部分比较难的工作,接下来,只需右键单击 tc1.tc,然后从弹出式菜单中选择 Transform > UML to C++,就可以在选定的项目中生成代码。
- 看一下
Route 类生成的代码。如列表1所示,它包括 startingPoint 和 endingPoint 属性,以及 getFare() 方法。
列表1. Route 类生成的代码文件 Route.h
#ifndef ROUTE_H
#define ROUTE_H
//Begin section for file Route.h
//TODO: Add definitions that you want preserved
//End section for file Route.h
struct Address;
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
class Route
{
private:
//@uml.annotationsderived_abstraction="platform:/resource/UML-1/
DWArticle-1.emx#_Hk7qMACuEdy9t-_gdCbefQ"
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
float runningCost;
//@uml.annotationsderived_abstraction="platform:/resource/UML-1/
DWArticle-1.emx#_hKd58ACuEdy9t-_gdCbefQ"
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
float expectedProfit;
//@uml.annotationsderived_abstraction="platform:/resource/UML-1/
DWArticle-1.emx#_ieiw8ACuEdy9t-_gdCbefQ"
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
Address * startingPoint;
//@uml.annotationsderived_abstraction="platform:/resource/UML-1/
DWArticle-1.emx#_jkEOkACuEdy9t-_gdCbefQ"
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
Address * endingPoint;
public:
//@uml.annotationsderived_abstraction="platform:/resource/UML-1/
DWArticle-1.emx#_kawmAACuEdy9t-_gdCbefQ"
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
float getFare()const ;
}; //end Class Route
#endif
|
如列表2所示, getFare 方法的默认内容已经生成。
列表2. Route.cpp
#include "Route.h"
//Begin section for file Route.cpp
//TODO: Add definitions that you want preserved
//End section for file Route.cpp
float Route::getFare() const
{
//TODO Auto-generated method stub
return 0;
}
|
修改代码
请注意,默认生成的内容一般不能够直接使用,因为 fare 总是 0 (zero),而这并不是一个有利的业务!所以需要将 getFare 方法的内容用列表3来代替。
列表3. Route.cpp
float Route::getFare() const
{
// Calcualte the fare
return runningCost * expectedProfit;
}
|
开发模型并再次运行转换
现在,在UML模型中为 Route 类添加另一个名为 print_disclaimer() 的方法。
- 设置返回类型为 String。
- 请注意,我们已为
getFare 方法添加内容,修改了Route.cpp文件。
- 使用相同的配置 (tc1.tc > Transformation > UML to C++)运行转换。
- 选择目标文件自动更新(默认选项)。
现在,一起来看看更新的代码(列表4)。
列表4. 更新的 Route.cpp
#include "Route.h"
//Begin section for file Route.cpp
//TODO: Add definitions that you want preserved
//End section for file Route.cpp
float Route::getFare() const
{
// Calcualte the fare
return runningCost * expectedProfit;
}
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
const char * Route::print_disclaimer()
{
//TODO Auto-generated method stub
return 0;
}
|
 |
关于 @generated 标记
请注意方法注释中的 <<@generated>>。方法体可以被自动保存。但是,如果您从模型中删除了该方法,然后再次运行 C++ 转换,那么这个方法和代码中的方法体都将会被删除。这样,就能够确保代码与模型的一致。
如果想要当模型中删除方法之后,代码中仍保存方法体,那么,您可以从方法注释中删除 <<@generated>> 标记。
|
|
请注意,修改的 getFare() 方法体已被保存。同时,模型中新添加的 print_disclaimer 方法已在 Route.h 和 Route.cpp 文件中生成新的代码。这样,您可以继续在代码中执行方法,同样地,可以对模型作增量修改。这在模型驱动迭代开发中相当有用的。
将 include 声明添加到保存节中。
print_disclaimer 方法体的代码如图5所示。请注意,我们使用了 cout 操作符,但 C++ 不能够识别。然而,代码是否能够正确解析,这是相当重要的,所以当转换再次执行的时候,应当将代码结构和方法体保存下来。如果想使这段代码可编译,您需要在 cpp 文件中添加 #include <iostream> 和 using namespace std;。当重复执行 UML 与 C++ 转换的时候,这些声明将会被保存下来。把这些声明插入到以下两条注释之间:
//Begin section for file Route.cpp
//End section for file Route.cpp
|
您添加到这个节中的内容将一字不漏地保存下来, 并且不需要 C++ 转换解析。
列表5. Route.cpp
#include "Route.h"
//Begin section for file Route.cpp
#include <iostream>
using namespace std;
//End section for file Route.cpp
float Route::getFare() const
{
// Calcualte the fare
return runningCost * expectedProfit;
}
//@generated "UML to C++ (com.ibm.xtools.transform.uml2.cpp.CPPTransformation)"
const char * Route::print_disclaimer()
{
const char *s = "No Refund once the ticket is purchased\n";
cout << s;
return s;
}
|
下载:
- UML 模型和 C++ 项目示例:选择 File > Import,然后按照 Import Existing Projects into Workspace 向导的提示操作,就可以将项目导入到 Eclipse 工作空间。
小结
本文讲述了使用 Rational Systems Developer 创建 UML model,然后生成 C++ 代码的基本步骤,以及保持代码与模型同步的方法。想要了解更多的信息,请参阅相关的链接资源。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| Sample UML Model project. | MyAutomobileFleet.zip | 7KB | HTTP |
|---|
| Final CPP Project | AutomobileFleetCode.zip | 33KB | HTTP |
|---|
参考资料 学习
获得产品和技术
讨论
作者简介  | 
|  | Sandeep Kohli 是位于 Bangalore 的 Rational Software Architect / Rational Systems Developer 项目组的开发工程师和构架师。他的工作涉及到多种 Rational 建模工具,比如 Rational Rose、Rational RoseRT 和 Rational Software Architect。此外,他还从事 C/C++/Fortran/Ada 编译器方面的工作。 |
 | 
|  | Sreerupa Sen 是 IBM 的一位架构师,主要从事 Rational UML 建模工具方面的工作。在她的软件职业生涯中,她曾跨足不同的领域,包括银行应用系统、中间件、实用数据中心,以及建模工具。在 IBM 她主要致力于 IBM Rational Software Architect / Rational Systems Developer,特别擅长使用 C++。 |
对本文的评价
|