ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

UML配套光盘模型迁移:XMI导出、代码生成与反向校验

UML配套光盘模型迁移:XMI导出、代码生成与反向校验

简介:《大象-Thinking in UML》配套光盘面向UML建模学习者、软件设计人员及需要复盘书中案例的读者,重点解决书中黑白插图查看、Rose建模过程还原和示例查阅不便的问题。压缩包共1903个文件,约9.25MB,以htm网页、tif/png/jpg/gif插图、pdf辅助读物及mdl、jar等建模与运行文件为主,并附txt说明和cnt目录索引,便于按图例、建模示例和阅读材料分类检索。已有573人学习下载。光盘内含全书彩色插图、可配合Rational Rose 2002及以上版本打开的建模示例原文件,以及无需安装Rose即可通过浏览器阅读的HTML版示例;还提供Java虚拟机安装文件与《OO系统分析员之路》PDF,方便读者理解建模过程组织方式、对照案例学习并补齐运行环境。

1. 一张十几年前的教学光盘,为什么现在还有人找它

某次内部评审,某开发者画的用例图被追问「这个 include 到底是复用还是分支」,会议室安静了十秒。散会后他翻出学生时代的 UML 教材想找书里那张标准图对照,才发现随书配套光盘早不知道丢哪去了。这也是《大象-Thinking in UML》配套光盘下载一直有人搜的原因:书里的用例图、类图、时序图不是凭空画出来的,配套工程里留着完整的模型文件、图源和示例代码,能看清一条线为什么这么连、一个关联为什么标了导航性。它解决的不是「背 UML 语法」,而是「照着别人一个能跑通的完整工程,把一张图从需求推到实现」。适合三类人:要从需求文档画出第一版用例图的新人,要给团队定建模规范、写进评审 checklist 的熟手,以及接手老项目、需要补一份设计文档的维护者。

2. 拆盘先分桶:六类文件与各自的打开姿势

拿到这张盘的压缩包,多数人的第一反应是双击里面那个 exe,然后在「安装失败」的弹窗里耗掉一个下午。更划算的顺序是先看目录结构、按扩展名分桶,搞清楚哪些文件是资产(模型工程、源码、图源),哪些只是证据(截图、讲稿、习题答案)。资产值得花时间迁移和转换,证据转成 PDF 归档就行,别在它们身上折腾。

2.1 按扩展名建一张对照表

先把整张盘的文件后缀列出来,对着下面这张表归类。表里的「首选」是保真度最高的打开方式,「备用」是在新机器上打不开时的退路。

扩展名实际内容首选打开方式备用方案转换后产出
.mdl某老牌建模工具的模型工程原工具 2003 版StarUML 导入、企业级建模工具XMI、PNG
.pdm / .oom / .cdm另一款建模工具的物理/面向对象/概念模型该工具 16.x先导 XMI 再进 StarUMLXMI
.vsd / .vdxVisio 图源Visio 2013 及以上在线版 Visio、先转 PDFPDF、PNG
.doc / .ppt讲稿、习题、章节电子稿OfficeLibreOfficePDF
.zip / .rar各章示例代码解压看工程文件后缀无可编译工程
.exe / .msi工具安装包(多为试用版)隔离虚拟机换开源工具无

分桶时有个判断标准很实用:能被另一个工具重新解析出类名、属性、关系的,算资产;只能看不能改的,算证据。按这个标准,一张盘里真正值得迁移的往往只有几十个文件,剩下的几百兆都是截图和安装包。

2.2 建模工程文件:三条导入路线与它们的代价

同一个 .pdm,有三种处理方式,代价完全不同,得先想清楚自己要什么再动手。

路线 A 是原工具打开。保真度最高,连图元摆位都能还原,代价是要养一台老系统虚拟机,而且试用版到期后很麻烦。我一般只在需要「原样截图给评审看」时才走这条。

路线 B 是 XMI 中转。XMI 是 OMG 定义的模型交换格式,只保留语义不保留布局——类还在、属性还在、继承关系还在,但图上的位置全丢。好处是导出的 .xmi 是纯文本,可以直接进 Git,团队里谁都能 diff。绝大多数场景我都走这条。

路线 C 是照着截图重画。只有在「要交付给客户、图必须好看」时才值得,一张复杂类图重画两小时起步。

路线 B 在建模工具里的操作步骤,大致是这五步:

  1. 在一台干净虚拟机里装建模工具(盘里通常是试用版,过期就换 StarUML,别去找激活工具,一是合规风险,二是那些包十有八九带东西)。
  2. File → Open打开 .pdm,第一时间File → Save As另存到工作目录,原件设成只读,后面所有操作都在副本上做。
  3. 进Tools → Model Options,把目标语言设成 Java 或 C#,字符集确认是 UTF-8。
  4. File → Export → XMI,导出类型选 XMI 2.1、编码 UTF-8,把「导出图形布局信息」这类选项关掉,导出的文件能小一半。
  5. 用 StarUML 的Import → XMI 2.1反向验证一次,能列出同样数量的类,再往下走。

如果是 .mdl 那条线,步骤类似但坑更多:老工具的 XMI 导出插件默认按 XMI 1.1 走,对中文类名支持很差,导出后类名经常变成Class_001。血泪经验是导出前先把模型里的中文类名批量改成英文,中文放到注释里,别指望格式转换能保住编码。

2.3 文档与图源:先转 PDF/PNG 再谈复用

.doc 和 .ppt 里贴的图,很多是从建模工具里以 OLE 对象方式粘进去的,双击能跳回模型——但那是在原机器上、原工具还在的前提下。换台机器,这些对象就是一张死图,甚至显示成「无法显示此对象」。

所以文档类的第一件事是批量转 PDF 存档,用 LibreOffice 的命令行一把梭:

# 把 docs 目录下所有 doc/ppt 转成 PDF,输出到 pdf 目录 soffice --headless \ --convert-to pdf \ --outdir ./pdf \ ./docs/*.doc ./docs/*.ppt

--headless表示不弹界面,服务器上也能跑;--convert-to pdf指定目标格式,换成pdf:writer_pdf_Export可以带更细的导出参数;--outdir必须提前建好目录,否则会静默失败。转换完抽查三份,重点看公式和嵌入的图有没有丢——老文档里的 MathType 公式经常被转成图片。

图源文件(.vsd)另一套处理:Visio 里文件 → 导出 → 更改文件类型 → PNG,分辨率选 300 dpi 以上,别用默认的屏幕分辨率,否则打印出来一片糊。导出前先做一次「替换字体」,把模型里用到但本机没装的字体统一换成系统自带字体,不然图元位置会整体偏移,这类问题在时序图上尤其明显,生命线会对不齐。

3. 清点与导出:把光盘变成可检索的资产库

光盘里最要命的问题不是打不开,是你不知道里面有什么。目录名常常是第7章、chap7、07-用例建模三种风格混着来,同一个示例被复制到三个目录下,改了其中一份另外两份还是旧的。所以在打开任何一个模型文件之前,先做一次静态清点。

3.1 先跑一遍指纹清点脚本

这个脚本只干三件事:统计后缀分布、算每个文件的 MD5、把结果导成 CSV。别小看 MD5,它能直接告诉你哪些文件是重复的,重复的那几份往往就是不同章节里抄来抄去的同一个示例。

# inventory.py —— 给整张光盘做一次静态清点 import csv import hashlib from collections import Counter from pathlib import Path ROOT = Path(r"D:\CD_UML") # 光盘挂载点或解压后的根目录 OUT = Path("inventory.csv") def md5_of(path, chunk=1 << 20): """分块算 MD5,避免大文件一次性读进内存""" h = hashlib.md5() with path.open("rb") as f: for block in iter(lambda: f.read(chunk), b""): h.update(block) return h.hexdigest() rows, counter = [], Counter() for p in ROOT.rglob("*"): if not p.is_file(): continue ext = p.suffix.lower() or "<none>" counter[ext] += 1 rows.append([str(p.relative_to(ROOT)), ext, p.stat().st_size, md5_of(p)]) with OUT.open("w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["rel_path", "ext", "size_bytes", "md5"]) writer.writerows(rows) # 按后缀看分布,先决定哪几类文件值得投入时间 for ext, n in counter.most_common(): print(f"{ext:>10} {n}")

chunk=1 << 20是一兆一块,机械硬盘和网络盘上都能跑;utf-8-sig是为了让 Excel 直接双击打开不乱码,这一点比省那几个字节重要得多;rglob会递归进所有子目录,光盘里嵌套三四层的压缩包解压目录也能扫到。

跑完先看两列:后缀分布里占比最高的那类文件决定你的工作量;md5列做一次去重,重复项只保留路径最短的那份,其余的移到_dup目录备份。做完这一步,几十个 G 的盘通常能瘦身一半以上。

3.2 模型转 XMI:一次导出,多工具复用

XMI 的价值在于「导出一次,到处都是入口」。同一个 .xmi,StarUML 能画图、脚本能解析生成代码、企业级建模工具能做影响分析。三家工具的导出位置和版本选项差别不小,参数没对齐就会出现「导进去了但关系全丢」。

工具导出菜单版本选择注意事项
老牌建模工具 AFile → Export → XMIXMI 1.1中文类名会丢,先改英文
建模工具 BFile → Export → XMIXMI 2.1 / 2.5关掉布局信息,文件更小
开源工具 CFile → Export → XMIXMI 2.1导入端对 2.5 支持一般

一个很常见的翻车:导出的 XMI 用文本编辑器打开,根节点写着xmlns:uml="http://schema.omg.org/spec/UML/2.1",但导入端按 UML 2.5 的命名空间去找,结果解析出零个类。遇到这种先看命名空间版本号,两边对不上就在导出选项里改,不要试图改文件里的 xmlns——改完校验和就废了。

3.3 目录与命名:跟书章对齐的落地规范

盘里原生的目录结构是按章节走的,但命名很随意。迁移到自己的工作区时,我固定用这套规则:ch<两位章号>-<图类型>-<业务场景>.<ext>,例如ch07-usecase-order-submit.pdm、ch07-usecase-order-submit.xmi。同名不同格式放在同一目录,一眼能看出「这是同一张图的模型和交换格式」。

大文件交不交版本库,取决于格式。XMI、源码、Markdown 这些纯文本直接进 Git;.pdm、.mdl、.vsd 这类二进制工程文件交给 LFS,否则每次提交仓库体积翻倍。

# 在工作区根目录执行,把二进制模型文件交给 LFS 管理 git lfs install git lfs track "*.pdm" "*.mdl" "*.vsd" "*.vsdx" git add .gitattributes git commit -m "chore: 模型二进制文件走 LFS"

git lfs install每台机器只需要跑一次,作用是写全局钩子;git lfs track改的是当前仓库的.gitattributes,必须提交上去队友才生效。忘了提交.gitattributes,队友 clone 下来拿到的还是指针文件,打开是一行version https://git-lfs...,这个坑我踩过不止一次。

4. 从图到骨架代码:类图批量生成与反向校验

配套光盘里的示例代码和模型是两份独立的东西:代码是手写的,模型是事后画的,两者早就对不上。想让它们重新对齐,得先明确哪些图值得转成代码,哪些转过去只是制造垃圾。

4.1 划清边界:哪些图值得转代码

不同图类型的可转换性差别很大,硬转只会得到一堆没人看的空方法。

图类型能否转代码实际产出建议
类图能属性、方法签名、继承关系值得做,一次生成长期受益
状态图能状态枚举 + 转移表值得做,转移表可直接驱动测试
时序图半能方法调用顺序注释只生成注释骨架,别生成逻辑
活动图半能流程伪码用来写文档,不进代码库
用例图不能测试用例清单转为验收用例更实际

判断标准很简单:有明确结构语义的图能转,描述行为顺序的图只能当参考。类图和状态图的语义是形式化的,转出来不会歧义;时序图里画的是某一次特定场景,转成代码就是把你锁死在那一条路径上。

4.2 用 XMI 生成类骨架的脚本

下面这个脚本从 XMI 里抽类、属性、继承关系,生成 Java 骨架。不同工具导出的 XMI 在命名空间和属性类型写法上不一致,脚本里做了两处兼容:去命名空间前缀,以及属性类型既认嵌套<type>也认type属性。

# xmi2skeleton.py —— 从 XMI 抽类与属性,生成 Java 骨架 import re import xml.etree.ElementTree as ET from pathlib import Path SRC = Path("model/export/design.xmi") OUT = Path("gen/java") def local(tag): """剥掉 {namespace} 前缀,只留本地标签名""" return tag.split("}")[-1] def strip_ns(root): """有些工具按 UML 2.1 导、有些按 2.5 导,统一去掉前缀再匹配""" for el in root.iter(): if "}" in el.tag: el.tag = el.tag.split("}", 1)[1] return root def camel(name): """把 order_no / Order No 统一成 orderNo""" parts = re.split(r"[_\-\s]+", name or "field") return parts[0].lower() + "".join(p.capitalize() for p in parts[1:]) root = strip_ns(ET.parse(SRC).getroot()) classes = {} for el in root.iter(): if local(el.tag) != "Class" or not el.get("name"): continue name = el.get("name") attrs = [] for sub in el.iter(): if local(sub.tag) != "Property" or not sub.get("name"): continue ptype = sub.get("type") or "Object" for t in sub.iter(): # 类型写在嵌套 <type href="#uuid"/> 里的情况 if local(t.tag) == "type" and t.get("href"): ptype = t.get("href").split("#")[-1] attrs.append((sub.get("name"), ptype)) classes[name] = {"attrs": attrs, "parent": None} for el in root.iter(): # 泛化关系单独收一遍 if local(el.tag) == "Generalization": child = el.get("child") or el.get("specific") parent = el.get("parent") or el.get("general") if child in classes and parent: classes[child]["parent"] = parent for cls, meta in classes.items(): head = f"public class {cls}" if meta["parent"]: head += f" extends {meta['parent']}" lines = [head + " {", " // 由类图生成,方法体待补"] for aname, atype in meta["attrs"]: lines.append(f" private {atype} {camel(aname)};") lines.append("}") path = OUT / f"{cls}.java" path.parent.mkdir(parents=True, exist_ok=True) path.write_text("\n".join(lines), encoding="utf-8")

几个参数要留意:SRC指向导出目录,别直接指光盘挂载点;OUT生成的目录建议加进.gitignore,它只是脚手架,手写代码不该和生成物混在一起;camel里把下划线和空格都当分隔符,是因为老模型里order_no和Order No两种写法同时存在。

生成的骨架只包含字段和继承关系,getter/setter 交给 IDE 一键生成。真正值钱的是类名和属性名对上了——评审时把生成的骨架和模型并排放,缺字段、多字段、继承链断裂一眼就能看出来。

4.3 反向工程:把代码回灌成图来做差异对比

正向生成解决「模型有没有落地」,反向工程解决「代码有没有漂移」。主流建模工具都支持从源码反向生成模型:File → Reverse Engineer → Object Language,选中 Java 源码目录,生成一个新的 .oom。

拿到反向生成的模型后,别急着覆盖原模型,先做集合对比。最简单的做法是导出两个模型各自的类名清单,做一次差集:

# 从两份 XMI 里各自抽出类名,排序后比对 grep -o 'name="[A-Z][A-Za-z0-9_]*"' model/design.xmi | sort -u > /tmp/a.txt grep -o 'name="[A-Z][A-Za-z0-9_]*"' model/reverse.xmi | sort -u > /tmp/b.txt comm -3 /tmp/a.txt /tmp/b.txt # 左列只在设计里有,右列只在代码里有

comm -3会输出两列不对齐的结果,左边那列就是「图上有、代码里没有」的类——通常意味着设计里的某个类被合并了或者干脆没实现。这类差异不需要全修,但每一条都得有解释,写进设计文档的变更记录里。

5. 避坑与排查:老光盘在新机器上的五个翻车现场

这一章是按我实际遇到过的顺序排的,从「打不开」到「打开了但用不了」,每条都按现象、原因、解决三段写。有些问题没有优雅解法,虚拟机就是最省事的答案。

5.1 装完了双击没反应,或者一闪就退

现象:安装程序跑完提示成功,双击图标闪一下就没了,任务管理器里进程起了一秒就消失。

原因:这类 2003 年前后的建模工具依赖 MDAC 和一组 32 位 ActiveX 组件注册,在 64 位新系统上注册环节会被静默跳过,程序启动时找不到组件就自己退了,不给任何提示。

解决:优先在虚拟机里用对应的老系统跑,这是唯一稳的路。一定要在物理机上试的话,用兼容模式(右键属性 → 兼容性 → 以兼容模式运行 → 选一个早期的系统版本)+ 管理员身份,然后手动注册数据访问组件:以管理员打开命令提示符,regsvr32 "C:\Program Files (x86)\Common Files\System\ado\msado15.dll",注册成功会有弹窗确认。装之前把杀毒软件的实时防护临时关掉,这类老安装器写注册表的动作经常被拦。

5.2 模型能打开,但中文全是乱码

现象:工程文件进去了,类名显示成????或者一堆方块,中文注释更是完全读不出来。

原因:老文件用本地编码保存,新版工具按 UTF-8 解释,双向对不上。还有一种情况是导出 XMI 时选了 1.1 版本,这个版本对非 ASCII 字符的处理本来就弱。

解决:先用旧版本工具打开,File → Save As另存一份,另存时注意看编码选项;如果工具没有编码选项,就走 XMI 2.1 + UTF-8 中转,在导入端重新解析。实在不行,把模型里的中文全部替换成英文拼音,中文写进单独的术语对照表——听起来土,但这是可维护性最好的做法,团队里没人会因为编码问题再返工。

5.3 图打开了,但元素位置全乱、连线对不上

现象:Visio 里的时序图,生命线歪七扭八,箭头指向错位,文字变成方框。

原因:原图用了本机没装的字体,Visio 找不到就用替代字体渲染,字符宽度变了,靠字符位置定位的图元自然全偏。这在等宽字体和对齐要求高的时序图上最明显。

解决:打开后先开始 → 替换字体,把所有非系统字体批量换成系统自带的黑体或宋体,一次替换完再看布局。如果盘里附带了字体文件,装完字体记得重启 Visio 再打开。导出 PNG 之前把页面缩放调到 100% 再看一遍,缩放状态下的「看起来正常」不作数。

5.4 导出 XMI 到一半中断,或者生成 0 字节文件

现象:点了导出,进度条走一半停住,目标目录里是个 0 字节的 .xmi;有时候是导出成功但导入端说「文件格式无效」。

原因:排前三的是路径里带中文或空格、模型里有非法字符的类名、以及目标目录没有写权限。另外模型本身如果有循环泛化(A 继承 B,B 又继承 A),导出器会直接卡死。

解决:把模型另存到一个纯英文短路径下再导,比如D:\work\m1\,别放在「我的文档\新建文件夹 (2)」这种路径里。导出前用工具的模型校验功能扫一遍,循环继承、空名类、孤立关联都会被列出来。权限问题最简单:换个目录试一次就知道。

5.5 打开旧工程,工具提示升级,升完发现文件改坏了

现象:新版本工具打开老工程,弹窗问「是否升级到新格式」,点了是,保存之后旧版本再也打不开,而且部分图形变成了空框。

原因:格式升级是单向的,新格式里的某些特性旧版本不认识,降级就会丢信息。最要命的是这个操作会直接覆盖原文件。

解决:升级前必须做两件事——原件设只读,以及Save As一个带版本号后缀的副本(如design-v16.pdm)。我现在的习惯是光盘里的任何工程文件,第一件事就是复制一份到工作目录并把原件属性改成只读,改完再动手。真改坏了也别慌,XMI 中转出来的语义版本通常能救回类结构,丢的只是布局。

6. 把它变成团队模板:三个沉淀动作

光盘里的东西看一遍就完了,价值有限。真正让它产生复利的是三个动作。

第一个动作是把示例工程做成模型基线。挑书里那张最完整的订单类图,把类名、属性、继承关系按团队的命名规范改一遍,导成 XMI 放进仓库的model/baseline/目录。以后新人画图,先照着基线改,比看十页规范文档管用。

第二个动作是让图和文档同源。文档里的图一律从 XMI 渲染,不要手工截图贴进去。图变了重新渲染一次,文档自动跟着变;截图贴进去的,三个月后没人说得清哪个版本是对的。

第三个动作是把评审 checklist 从图里长出来。每次评审踩到的坑记一条,攒够十条就是团队的建模规范。比如「每个 include 关系旁边必须写清楚复用理由」「聚合和组合不许混用」——这些不是从书上抄的,是从具体的翻车现场长出来的。

验证方法也很朴素:随机抽三个用例,从用例图追到时序图再追到类图,最后在代码里找到对应的方法。追不通的链路,就是模型和实现对不上的地方。这个动作我每季度做一次,每次都能捞出几个漂移已久的类。从那以后我每次接手一个带模型的老项目,第一件事都是先跑一遍清点和导出、把模型拉进仓库,再谈别的。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表