ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

面向对象分析与UML类图:五种关系详解与工具实操

2026/9/30 13:43:14 拓冰建站 浏览量
面向对象分析与UML类图:五种关系详解与工具实操 1. 类图在面向对象分析里到底站在什么位置刚入行那会儿我对类图的理解特别肤浅觉得它就是一堆方框加箭头画出来给领导看的技术画。直到有一次接手一个已经跑了三年的老系统改造前任留下的文档几乎为零唯一能找到的就是几张泛黄的类图截图我才真正意识到——类图不是装饰品它是整个面向对象分析过程中承上启下的骨架。今天这篇就围绕面向对象分析和类图这两个核心把我这些年踩过的坑、总结的方法、用过的工具都摊开来聊。先说清楚这个东西是什么。面向对象分析OOA本质上是一个把现实世界翻译成对象世界的过程而类图Class Diagram是UML里用来描述系统中类、接口、它们之间的静态结构关系以及属性、方法的一张结构化视图。它不管你代码怎么跑、时序怎么走只管一件事这个系统里有哪些东西这些东西各自长什么样彼此之间又是什么关系。换句话说类图回答的是系统由什么组成而不是系统怎么运转。它能解决什么问题最直接的三个第一团队沟通有了统一语言产品经理说订单关联多个商品开发脑子里浮现的是一对多关联线不会各说各话第二代码设计有了提前推演的地方哪些类该抽象、哪些关系该用组合、哪些地方会形成循环依赖在画图阶段就能暴露出来第三重构和交接有了依据一张清晰的类图比几千行注释更能让新人快速理解系统结构。适合谁来参考如果你是刚学面向对象的学生这篇能帮你把书本上抽象的概念落到具体箭头和代码上如果你是工作两三年的开发想从能写代码进阶到会做设计类图是你绕不开的基本功如果你是带团队的技术负责人怎么让组员画出有用而不是好看的类图这里面的取舍经验应该对你有帮助。接下来我会从设计思路、核心关系拆解、工具实操、活动图配合、实战案例、问题排查几个层面,一层层往下讲。2. 类图的整体设计思路与建模取舍2.1 为什么类图要先做减法再做加法很多人画类图有个通病一上来就想把所有类都塞进去结果画出来像一张蛛网密密麻麻谁也不敢看。我的经验是反过来——先做减法再做加法。具体来说第一轮只识别核心领域概念把跟当前分析目标无关的类全部砍掉。比如你分析的是订单模块那用户权限、日志埋点、缓存策略这些先别画它们属于另一个关注点。这个思路背后的逻辑是关注点分离。一张类图对应一个分析视角视角越多图越乱。你完全可以用三张类图分别描述领域模型、服务层结构、数据访问层结构各自清晰。我见过一个团队硬生生把整个电商系统塞进一张A4纸大小的类图里结果开会讨论时没人能找到自己关心的那个类这就是典型的贪多嚼不烂。第二个取舍点是抽象层级要统一。同一个层次的东西放一起画比如订单商品用户都是业务实体层级一致如果你把订单和订单表的自增主键生成器画在一起层级就错乱了。判断标准很简单问自己这些类是不是在同一个业务抽象层面上说话不是的话就分开画。2.2 类的识别从名词到对象的翻译方法识别类这件事最土但最有效的办法是在需求描述里圈名词。拿起一段需求文字把所有名词和名词短语圈出来比如系统需要管理客户信息客户可以下多个订单每个订单包含若干商品商品属于某个分类。圈出来的是系统、客户、订单、商品、分类。然后做一轮筛选——系统这种太泛的去掉剩下的客户、订单、商品、分类基本就是候选类。但光靠圈名词是不够的这只是起点。接下来要做一轮职责校验这个候选对象有没有自己的状态属性和行为方法如果它只是一个纯数据容器可能更适合做成属性而不是独立类如果它有明确的行为和生命周期那它就是合格的对象。举个例子地址在有些场景下就是客户表里的几个字段但如果你需要单独管理收货地址簿、支持一个用户多个地址、地址还要做校验和格式化那地址就值得单独成类。这里有个我自己总结的判别口诀分享给你有状态、有行为、有身份、有生命周期四个里占两个以上就单独成类只占一个就考虑降级为属性。这个口诀不绝对但能帮新手快速做判断避免把类图画成字段的堆砌。2.3 分析阶段和设计阶段类图的差异同一个系统分析阶段的类图和设计阶段的类图长得完全不一样很多人分不清这一点。分析阶段的类图是概念类图只描述业务概念和它们的关系不关心技术实现。比如分析阶段只有订单——商品的关联最多标个多重性一个订单对应多个商品。到了设计阶段类图就要落地了你得引入接口、抽象类、工厂类、仓储类关联关系也要明确用集合还是数组、用组合还是聚合、要不要引入中间表。我的建议是分两轮画第一轮画概念类图跟业务方对齐确保对领域理解一致第二轮画详细设计类图跟开发对齐明确每个类的方法签名和依赖方向。两轮图不要混在一起否则业务方看不懂技术细节开发又觉得业务图没营养。2.4 类图的边界在哪里什么时候该停笔画类图最大的诱惑就是再细化一点。但类图不是越细越好写到方法级别的类图其实已经接近代码了这个时候继续画反而浪费时间不如直接写代码。我的经验边界是类图描述到方法签名这一层就够了方法内部怎么实现不要画那是活动图或者时序图的活儿。类、接口、属性、关键方法签名、类之间的关系画到这类图的使命就完成了。3. 类图的核心元素与五种关系拆解3.1 类、属性、方法的标准表示法一个标准的类在UML里用三层矩形表示最上面是类名中间是属性最下面是方法。属性写法一般是可见性 名称: 类型比如- id: Long前面的减号表示私有private加号是公有public井号是保护protected波浪号是包内可见。方法也是类似的格式 方法名(参数): 返回值。这里说几个新手容易忽略的细节。第一类型可以省略在概念类图里只写属性名就够了但详细设计阶段建议写全类型否则开发还得猜。第二静态成员要加下划线这个很多人不知道静态属性和静态方法在UML里是用下划线标注的。第三抽象类名和方法名要用斜体这能一眼区分哪些是不能直接实例化的。属性下面那块有时候会有人写上getter/setter之类的东西我的建议是别写那是Java的语法糖不是设计层面的信息写了只会让图变脏。真正值得填进去的是业务上有意义的方法比如订单类的calculateTotal()、canCancel()这些才是设计要表达的内容。3.2 五种关系对照依赖、关联、聚合、组合、继承这是类图最核心也最容易搞混的部分我按耦合强度从弱到强排一下顺便配上代码感受一下区别。依赖Dependency用带开放箭头的虚线---表示意思是A在某个方法里临时用到了B。这是最弱的耦合代码上通常表现为方法参数、局部变量、静态方法调用。比如一个OrderService里调用了DateUtil.format()这就是依赖关系。关联Association用实线表示是A长期持有B的引用。比如Order里有个User user字段这就是关联。关联可以标注多重性1、0..1、1..、也可以加角色名。聚合Aggregation用带空心菱形的实线表示菱形指向整体。它是一种特殊的关联强调整体-部分但部分可以独立于整体存在。比如班级和学生班级解散了学生还在这就是聚合。组合Composition用带实心菱形的实线表示也是整体-部分但部分不能独立于整体存在。比如订单和订单项订单删了订单项就没意义了这就是组合。组合是最强的关联。继承Inheritance/Generalization用带空心三角的实线表示箭头指向父类。子类继承父类的属性和方法这是is-a关系。关系类型图形符号耦合强度代码表现典型场景依赖虚线开放箭头最弱方法参数/局部变量工具类调用关联实线弱成员变量订单持有用户聚合空心菱形实线中成员变量可独立班级含学生组合实心菱形实线强成员变量同生命周期订单含订单项继承空心三角实线最强extends支付方式继承3.3 五种关系画出类图并观察区别的实操练习光看表格记不住我建议你找个真实场景练一遍设计一个公司-部门-员工-项目的模型。公司聚合部门部门可以独立存在、部门聚合员工、项目关联员工一个员工可以参与多个项目、项目经理是员工的子类继承、项目里用到了日期工具类依赖。把这五种关系在一张图里画全看看箭头和菱形怎么摆。关键是观察两个地方一是聚合和组合的菱形方向空心/实心菱形永远指向整体那一端很多人会画反。二是多重性的位置多重性标在关系线的两端表示这一端的类有多少个实例对应另一端。我见过太多人把多重性标错位置一个订单有多个商品多重性1..*应该标在商品那一端表示商品端有多个而不是订单端。代码上怎么体现区别聚合和组合在Java代码里可能都是成员变量区别在生命周期管理上。组合关系里整体创建时会new出部分整体销毁时部分跟着销毁聚合关系里部分由外部传入整体只是持有引用。这一点在类图上不会直接写出来但你画图时脑子里必须有这个生命周期概念否则画出来的组合和聚合就是形似神不似。3.4 接口、抽象类与泛化的表达除了类之间的五种关系类图里还经常出现接口和抽象类。接口用带箭头的虚线加一个「interface」标记实线箭头指向它表示实现Realization。抽象类跟普通类一样画只是类名和抽象方法用斜体。这里有个实战经验接口和抽象类在类图上很容易混淆尤其是有些工具不显示构造型stereotype标记的时候。我的建议是用StarUML或IDEA这类工具时确保打开构造型显示接口一定标上「interface」这样团队看图的歧义会少很多。如果是手绘或用Visio就在接口名上方手写标注别偷懒。泛化继承和实现接口的区别也要注意泛化是空心三角实线实现是空心三角虚线。前者表示是一个后者表示能做一个。一个类可以继承一个父类但实现多个接口画的时候箭头会集中指向不同的目标这时候图的布局就很考验功底了建议把父类放上方、接口放右侧让线条尽量不交叉。4. 主流工具实操从StarUML到IDEA的正反向出图4.1 StarUML画类图的完整步骤StarUML是我个人最推荐的独立建模工具界面清爽五种关系的符号都很规范。具体步骤打开StarUML新建Model右键添加Class Diagram然后从左侧工具栏拖拽Class元素到画布。双击类可以编辑名称、添加属性和方法在属性编辑区按照可见性 名称: 类型的格式一行一个填。画关系的时候工具栏上有对应的箭头按钮——Association、Aggregation、Composition、Generalization、Dependency 一字排开。点中关系按钮从源类拖到目标类即可。画完之后右键关系线可以设置多重性、角色名、导航性。这里有个坑StarUML默认画出来的是双向关联如果你只想表达单向导航需要右键关系线把Navigable勾掉一端否则开发会误以为双向依赖。导出方面StarUML支持导出为图片、PDF也支持生成Java代码骨架。我一般会把类图导出成PNG贴进设计文档同时保留.mdj源文件方便后续修改。注意StarUML新版本是收费的学生可以用开源替代品如draw.io或者早期的StarUML版本。4.2 IDEA生成类图从代码反向出图如果你的项目已经是写好的代码想在IDEA里生成类图操作其实很简单在项目视图里选中一个包或者几个类右键选择「Diagrams」-「Show Diagram」IDEA就会自动生成一张类图。但这只对已编译的代码或者已索引的源码有效如果依赖没解析成功生成的图会缺关系。IDEA生成的类图默认只显示当前包的类如果要显示关联的类需要右键图里某个类选择「Show Dependencies」它会递归展开。快捷键方面CtrlAltShiftU是打开图表的常用组合CtrlAltU是弹出预览。实测下来IDEA的类图适合快速理解现有结构但它对多重性、角色名的支持很弱基本只是谁继承谁、谁引用谁的物理关系不适合做正式设计文档。另外提醒一句IDEA社区版对UML图的支持是有限的有些高级的依赖分析需要旗舰版。如果你的IDE是社区版打不开图表功能别以为是项目问题是版本限制。4.3 Eclipse查看类图插件的配置Eclipse本身不带类图功能需要装插件最常用的是ObjectAid UML Explorer。安装方式是在Help-Install New Software里添加ObjectAid的更新地址装完之后新建一个.ucls文件把要分析的类拖进去它就会自动生成类图。Eclipse这套方案的优势是实时联动你改了Java代码类图会跟着更新适合做代码结构监控。缺点是只能展示已编译的类而且关系识别比较保守泛化、实现能识别聚合和组合它一律当作普通关联基本分不出来。所以用它做快速浏览可以做正式设计还是要靠StarUML这类建模工具。4.4 用Visio画UML类图的方法Visio画类图很多人第一反应是找不到模板。实际上在新版Visio里可以搜索「UML」找到「UML类图」模板里面已经内置了类、接口、各种关系连接线。如果搜不到用「软件和数据库」分类下的「UML模型图」也能凑合。用Visio画类图的流程是拖一个Class形状到画布在形状数据里填类名、属性、方法然后从工具箱里选关系线连接。Visio的一个好处是排版能力强适合画大图然后打印贴墙团队评审的时候很直观。缺点也明显它不生成代码改起来纯手工而且关系线的多重性标注需要手动添加文本框容易漏。我个人的工具选择建议是要精准表达五种关系用StarUML要看现有代码结构用IDEA或Eclipse插件要排版好看贴文档用Visio。三者不是替代关系看场合用。5. 类图与活动图的配合使用5.1 类图和活动图各自负责什么单独说类图容易让人产生一个误解以为一个系统画完类图就完了。其实类图只描述静态结构它回答不了这个下单流程怎么走。这时候需要活动图Activity Diagram出场。活动图描述的是业务流程或算法步骤用开始节点、动作、判断、合并、结束节点把流程串起来。两者的分工可以用一个比喻概括类图是剧组名单告诉你有演员、导演、道具师这些角色以及他们之间的关系活动图是分镜脚本告诉你每一场戏谁先出场、做什么动作、遇到分支怎么走。你做面向对象分析的时候两个都要用先用活动图梳理业务流程再从流程里识别出参与的对象最后用类图固化对象结构。5.2 从活动图反推类的实战流程这个配合怎么落地我拿一个用户下单的流程举例。先用活动图画用户浏览商品 - 加入购物车 - 提交订单 - 系统校验库存 - 校验通过则生成订单 - 扣减库存 - 发起支付 - 支付成功 - 更新订单状态。画完活动图你开始从每个动作里找名词和动词。动作生成订单提示你需要一个Order类校验库存提示需要一个InventoryService扣减库存说明Inventory有deduct()方法发起支付说明需要一个PaymentService和Payment对象。这样从流程节点一个个映射到类最后再整理成类图比凭空想象要靠谱得多。这个方法的精髓在于活动图帮你发现了隐含的服务类。很多人画类图只画得出业务实体画不出服务类原因就是只从名词出发忽略了动作。而动作往往对应服务类的方法这个线索只有流程分析才能给出。5.3 两种图在同一文档里的组织方式如果要把两种图放进同一份设计文档我的排版习惯是先放活动图讲业务怎么流转再放类图讲对象怎么组织中间用一段文字过渡说明从流程中识别出了哪些核心对象。这样读者先建立动态认知再建立静态认知理解成本最低。要避免的是把活动图和类图画在同一页或者同一张画布里那样信息密度太高读者眼睛不知道往哪放。宁可多分几页也别挤在一起。6. 实战案例从需求到类图的完整推演6.1 需求梳理与候选项识别我拿一个简化版的图书馆借阅系统做案例需求描述是读者可以借阅图书一个读者可以同时借多本书每本书有唯一ISBN图书分为纸质书和电子书两种借阅有借出日期和应还日期逾期会产生罚金管理员负责处理借还操作。先圈名词读者、图书、ISBN、纸质书、电子书、借阅记录、借出日期、应还日期、罚金、管理员。再做职责校验ISBN是属性不是类借出日期和应还日期是借阅记录的属性罚金可以是借阅记录的方法计算出来的属性纸质书和电子书是图书的子类管理员是一个独立角色。最终候选类Reader、Book、PaperBook、Ebook、BorrowRecord、Admin。6.2 关系判定与箭头选择接着判关系。BorrowRecord和Reader是关联一个读者有多条借阅记录记录持有读者引用BorrowRecord和Book是关联一条记录对应一本书PaperBook和Ebook都继承Book泛化空心三角指向BookAdmin和BorrowRecord是依赖或关联取决于管理员是否需要长期持记录一般用关联更稳妥。这里有个细节要注意借阅记录和图书到底是关联还是组合我的判断是关联因为书被还回来之后还在系统里不会因为记录删除就消失。如果误判成组合代码里就会写成删除借阅记录时把书也删了那是灾难性的bug。这个判断依据就是前面说的生命周期原则。类图的关键属性也要补上Book有isbn、titleBorrowRecord有borrowDate、dueDate还有一个calculateFine()方法Reader有id、name。6.3 布局优化与可读性处理图画完了不代表就能交付布局才是让图能看的关键。我的布局原则有三条继承关系从上往下排父类在上子类在下强耦合的类放一起组合、聚合的两端靠近关系线尽量不交叉交叉了就把类挪一挪。具体到这个案例我把Book放顶部PaperBook和Ebook并排在下方中间放BorrowRecord它同时连向Reader和BookAdmin放在右侧。这样线条走向清晰读者一眼就能看懂结构。如果关系线还是乱可以考虑拆分成两张图——一张画图书体系一张画借阅体系用一张简图连接。6.4 从类图到代码骨架的落地类图最终要能指导编码。以BorrowRecord为例类图告诉开发这个类关联Reader和Book有borrowDate、dueDate属性和calculateFine()方法。翻译成Java大概是public class BorrowRecord { private Reader reader; private Book book; private LocalDate borrowDate; private LocalDate dueDate; public BigDecimal calculateFine(LocalDate returnDate) { if (returnDate.isAfter(dueDate)) { long overdueDays ChronoUnit.DAYS.between(dueDate, returnDate); return FINE_PER_DAY.multiply(BigDecimal.valueOf(overdueDays)); } return BigDecimal.ZERO; } }看到没类图画对了代码骨架几乎是自动生成的。这也是为什么我说类图是设计与实现之间的桥梁——它就是代码的地图地图画准了后面的路就好走。7. 常见问题排查与避坑实录7.1 类图关系画错的典型症状与修正症状一聚合和组合的菱形画反了。空心或实心菱形必须指向整体那一端。我见过很多人画订单项-订单关系时菱形指向订单项这是错的。修正方法读一遍关系问自己谁包含谁包含方就是菱形那一端。症状二多重性标在了错误的一端。记住多重性描述的是视线从这一端看向另一端时另一端有几个实例。一个订单有多个商品站在订单看商品是多个所以*标在商品端。不确定的时候就默念这一端的类对应另一端的几个对象。症状三把依赖画成了实线。依赖必须是虚线这个没有例外。如果两个类之间只是方法调用关系永远是依赖画成实线关联会让耦合关系被高估。症状错误画法正确画法检查方法菱形方向反菱形指向部分菱形指向整体问谁包含谁多重性错位标在关系错误端标在对应实例多的一端念这端对应几个那端依赖误画关联实线虚线开放箭头判断是否长期持有引用实现误画继承实线空心三角虚线空心三角判断是is-a还是can-do7.2 工具使用中的高频坑StarUML的自动双向关联坑前面提过再强调一次画完一定要检查导航性。IDEA生成的类图缺关系多半是因为依赖的jar没索引或模块没编译重新构建一下项目通常能解决。Eclipse插件不显示组合聚合这是插件能力限制不是你的错需要精确表达就换工具。Visio找不到UML模板可以试试用基本流程图里的矩形加手工连接线虽然不规范但应急能用。7.3 我的实操心得最后分享几条我个人沉淀下来的经验。第一类图一定要跟代码同步代码改了图不改图就成了误导人的历史文物。我现在的做法是核心模块的类图跟着版本走每次重构必须更新。第二给类图加个版本号和日期放在图的角落。这样团队看的时候能判断这张图是不是最新的避免拿旧图指导新开发。第三不要追求一张图覆盖全部拆成多张反而更清晰。我经手过的最好的一个项目类图分了领域模型、服务层、数据层三张每张都干净利落新人半天就能上手。第四画之前先跟业务方对一遍概念否则你画的类名跟业务嘴里说的完全不是一回事评审时会被反复打回。先对齐词汇表再动笔能省一半时间。第五五种关系的区别最好的记忆方式是动手写代码。把同一个场景用依赖、关联、聚合、组合、继承分别写一遍代码写完你自然就记住区别了比背十遍图形符号管用。