资讯详情

资讯详情

UML对象图怎么画:对象、链与多重性校验实战

学 UML 那会儿我背得最熟的一句话就是“对象图是类图在某一时刻的实例快照”考试能默写一让动手画就卡壳对象名要不要写全下划线到底画哪两个框之间那根线叫关联还是叫链后来带新人发现这个坎几乎人人都会遇到——概念都懂落笔就废。这篇文章我拿一个虚构的航空购票系统当靶子把UML 对象图里最容易含糊的两件事——对象和链——拆到能直接下笔的程度。你看完不用再背定义照着订单、机票、座位这几个实例连一遍五分钟之内能自己画出一张能交差的图。适合刚学 UML 的学生、要写课程设计的技术文档岗、以及被要求“补一张对象图”但完全不知道从哪下手的开发者。1. 先把“对象图”这三个字拆开看1.1 对象图是时间切片不是流程动线很多人第一次画错不是语法错是心法错——把对象图画成了“用户下单到出票”的流程。对象图的本质是某一秒按下快门那一刻系统里存在哪些对象、每个对象的属性取什么值、哪些对象之间连着线。它不回答“先发生什么后发生什么”只回答“此刻长什么样”。所以判断自己画得对不对有个很土但特别有效的自检法用“此刻”造句。你画的每一根线都能念成“此刻订单 o1001 关联着乘客 p2001”那就是对象图如果有一根线你得念成“然后系统把订单状态改成已支付”那它就该出现在时序图或活动图里不该待在这。这个区分不是学院派洁癖。实际工作里对象图最常见的用途是核对核对类图的多重性写得对不对、核对接口返回的数据结构是不是这个层级、核对某条业务规则在数据上到底成不成立。这三件事都要求“静止且具体”。一旦画面里混入时间顺序核对就无从下手——你没法验证一个“流程”是否符合约束只能验证一个“状态”是否符合约束。我在评审里见过一张画了七个对象的图从“乘客点击查询”一路画到“收到短信”中间还有箭头标注“等待支付超时”。这张图的信息量其实不小但它不是对象图它是一张信息量不足的时序图。作者花了两个小时最后还得重画一张真正的快照。这个弯路不值得走第二遍。1.2 对象和类的分水岭有没有具体的值类和对象的区别教科书上写着“抽象与具体”听着对但不好用。我给一个更硬的判据看它有没有具体的值。类是“乘客有姓名、证件号、手机号这几个字段”对象是“乘客 p2001姓名是 A 乘客证件号是 X 段脱敏字符串手机号是 138 开头的一串”。类里写的是字段名和类型对象里写的是字段名和取值。这一条判据能把 90% 的犹豫直接砍掉拿不准该画哪个框的时候问自己“我能不能给这个框写出具体的属性值”能就是对象不能就是类。还有一条常被忽略的对象只呈现属性不呈现操作。你在对象图上写cancel()、pay()这类方法名基本等于在简历上写错自己的名字。原因是对象图要表达的是“数据此刻的状态”方法的定义属于类的职责快照里没有“方法正在执行”这回事。有些教材为了强调“对象有行为”会在对象框下面留空的操作区这在严格规范里是可以的但我不建议——留了空区总有人忍不住往里填东西填了就是错。顺带说一个实战里的取舍。真实项目中对象图的属性槽位slot到底写多细我的经验是只写跟这张图要说明的问题相关的属性。如果你这张图的目的是说明“订单与机票的一对多关系”那机票的票价、税费、退改规则统统可以省略写个status ISSUED就够了。写全反而让图变噪音。但如果你这张图是要拿去做测试数据的基准那就得写全因为测试用例要的是完整报文。目的决定粒度别为了“看起来完整”把图撑爆。1.3 什么场景才值得画一张对象图不是所有项目都需要对象图。我做过的项目里大概只有三类场景会真的画它画了也确实省事。第一类是复杂关联的自证。类图上写了一个限定符qualified association或者一个关联类是三元关联光看类图很难判断到底怎么落地。这时候给出一个具体实例用真实数据把线连一遍看不懂的人立刻就看懂了。对象图在这儿的价值是“举例”它比任何文字解释都直观。第二类是多重性与导航性的一致性校验。类图是设计阶段的自我承诺承诺“一个订单至少有一张机票”“一张机票只对应一个座位”。这些承诺在真实数据里成不成立画一张快照数一数线的数量立刻见分晓。这个用法在评审中最见效因为它把抽象的约束变成了可数的物理事实。第三类是测试数据与接口结构对齐。这条最被低估。一张画得规范的对象图本质上就是一份带类型标注的测试数据说明书对象的类型告诉你这个节点在代码里是什么实体属性槽位告诉你字段名和取值示例链告诉你谁引用谁。前端照着它写 mock后端照着它写 DTO测试照着它写 fixture三方对同一张图能省掉大量扯皮。反过来说如果你的图只是把类图里的类名后面加个数字属性槽位全写“xxx”那画不画都一样纯属凑页数。2. 对象与链的语法那些教科书一句话带过的细节2.1 对象框的三种写法与那根下划线对象框的写法规格里其实是三种很多人只知道第一种。第一种具名对象对象名 : 类名比如o1001 : Order。这是最常用的写法对象名是这张图内部的标识不需要跟数据库主键一致只要在同一张图里唯一就行。我习惯用“类名缩写 序号”因为改图的时候不容易重名。第二种匿名对象只写: 类名冒号前面留空。什么时候用当这个对象在这张图里只是“一个占位”它的身份对要说明的问题不重要时。比如你要说明“订单里有一张机票”那张机票具体是哪一张无所谓就可以匿名。但我一般不建议匿名理由很实在一旦图里出现两个同类的匿名对象读者就没法在文字说明里指代它们了只能说“那个左边的机票”沟通成本陡增。第三种只有对象名、不写类名o1001。这种写法在规范里是合法的因为对象名在图内已经声明过类型。但它有个致命问题——脱离上下文就没法读。评审稿、截图、PPT 里流传的对象图一定会有脱离上下文的时候。所以我个人的规矩是发给别人的图类名一律不省。至于那根下划线规范要求对象名整体带下划线用来跟类名区分。手绘和很多轻量工具会省略考试和正式文档里最好加上。这不是形式主义在一张既有类框又有对象框的混合图里下划线是唯一的视觉区分手段省了它读者要逐字读才能分辨。2.2 属性槽位怎么写才不丢信息属性槽位slot的写法是属性名 值看起来简单但三个细节决定了图的专业度。第一个细节值的类型要能自证。写amount 1280.00比amount 1280好因为小数点暗示了它是金额写createTime 2024-05-18 09:12:31比createTime 今天上午好一万倍。字符串值建议带引号status PAID比status PAID清楚因为它告诉读者这是个枚举字符串而不是一个叫 PAID 的对象。这些小事堆起来就是一张“能被人直接照抄成代码”的图和一张“只能看个大概”的图的区别。第二个细节多值属性的写法。如果一个属性是集合比如订单的明细列表规范允许写成items [t1, t2]这种形式用对象名引用图中的其他对象。这个写法非常有用因为它同时表达了“这个集合里有哪些元素”和“这些元素在图上的哪个位置”。但要注意集合属性的元素如果本身就是对象那它和链是两种不同的表达路径。用链表达是“关联”用属性表达是“聚合/组合”。选哪条路取决于类图上它是关联还是组合别在对象图上临时改主意。第三个细节空值和无值的区别。payment null表示“这个位置查过了确实没有”而不写这个槽位表示“这个问题跟本图无关我没有讨论”。这两个意思完全不同。我在评审里见过因为混淆这两者引发的争论一方坚持“订单没支付所以没有支付记录”另一方坚持“你图上没画不代表系统里没有”。写个null能省掉半小时的口水。2.3 链是关联的实例不是随便一根线现在说正题——链link。定义一句话链是关联association的实例就像对象是类的实例。这句话的分量在于它给链上了一道紧箍咒你不能凭空连一根类图上不存在的线。类图里 Passenger 和 Order 之间没有关联你在对象图里把 p2001 和 o1001 连起来那是错的哪怕业务上它们“确实有关系”。关系没建模就是类图欠了债该回去补类图而不是在对象图上打补丁。链的表示很简单两个对象之间一条实线。线两端可以标注角色名就是关联端的名字比如下单人、所属订单和多重性。角色名建议标因为一条线两端的语义不对称——“订单属于乘客”和“乘客拥有订单”读起来方向相反角色名能消除歧义。多重性标不标看场景如果是拿去做多重性校验的图必须标而且要跟类图逐一对照如果只是示意可以不标但我会标因为标注的过程本身就是一次复核。有个高频误区得澄清链没有名字。很多人习惯给线加个标签叫“购买”这是把关联名搬到链上了。关联可以有名字链没有。链能带的只有两端的角色名和多重性。这个小规矩在正式文档里挺要紧因为审核的人一眼就能看出你懂不懂。再补一个冷门但会遇到的n 元关联的链。三个及以上对象之间的关联链要用菱形连接菱形中间写关联名。这种图很少画一旦画出来通常是说明“某类订单同时涉及乘客、航班、支付渠道三方”这类复杂约束。真遇到了别硬画成三条二元线那表达的是三个独立关联语义完全不同。2.4 多重性、导航性、限定符在快照里的落地校验对象图最硬的用处就是把类图上的三个约束拉出来“验货”。多重性校验最好理解类图上写Order 1 —— 1..* Ticket那快照里任何一个订单对象连出去的机票链数量必须 ≥ 1。如果 o1001 一条机票链都没有这张图就自相矛盾——要么数据违规要么类图写错了。校验方法是机械的给每个关联端数线。数完记录下来比“感觉差不多”靠谱得多。导航性校验稍微绕一点。类图上如果标了单向导航Order → Ticket意思是订单能拿到机票机票拿不到订单。那在对象图里链的方向就应该体现出这种可访问性。有些工具会用箭头表示有些不表示。我自己的做法是在文字说明里点一句“该链按类图定义为单向可导航反向访问需通过订单查询”。这样即使图上看不出来读者也不会误用。限定符校验是最容易被忽略的。类图上如果写了Order [orderNo] —— Ticket意思是订单通过订单号来限定访问它下面的机票。那快照里就必须出现这个限定值而且它得跟订单对象的orderNo属性槽位一致。这种一致性检查在纸面上很容易做在实际代码里却经常出问题——同一个值在两处维护早晚会不同步。对象图把这个隐患提前暴露出来算是额外收益。3. 拿航空购票系统开刀从类图到对象图的全过程3.1 先定类图骨架这一步偷懒后面全崩对象图是类图的实例化所以类图缺什么对象图就画不出什么。这个航空购票系统我抽了七个核心类全部虚构不涉及任何真实机构。类名关键属性说明Passenger 乘客passengerId、name、idNo、phone下单主体Order 订单orderNo、createTime、status、amount一次购买行为的容器Ticket 机票ticketNo、fare、status、passengerType单张客票Flight 航班flightNo、departTime、arriveTime、status承运班次Seat 座位seatNo、cabinClass、status航班上的物理座位Payment 支付paymentNo、channel、amount、status支付流水Airport 机场code、city起降点关联我定了六条都是这个系统里绕不开的乘客 1 对 0..* 订单订单 1 对 1..* 机票机票多对 1 航班机票 1 对 1 座位订单 1 对 0..1 支付航班多对 1 机型并且航班关联两个机场出发、到达各带角色名。这里有个设计取舍值得说机票为什么单独成类而不是塞进订单里当明细因为机票有自己的生命周期——它可以单独退、单独改签退改之后订单还在只是里面少了一张票。如果做成订单的从属属性退票就得改订单结构状态机立刻复杂三倍。这个判断标准我总结成一句话能独立变化的东西就该独立成类。这条标准也解释了为什么座位要独立——同一个座位在不同航班上是不同实体而且“座位被占”这个状态需要独立追踪。3.2 选一个真实有意义的快照时刻定完类第二步不是画是选时刻。一张对象图只能对应一个瞬间选错了瞬间图画得再规范也没说服力。我选的是支付成功、机票已出、乘客尚未值机的那个瞬间。理由有三个。一是这个时刻对象最全乘客、订单、机票、航班、座位、支付全在七类对象能连成一张完整的网演示价值最高。二是这个时刻的约束最多多重性、唯一性、状态一致性都能验正好拿来当反面教材的对照。三是它贴近真实业务里最常被讨论的那一段——出票成功但还没出行客服、对账、改签三个场景都要看这个状态。顺带说个经验别选“刚创建订单还没支付”当示例。那个时刻对象太稀疏订单是空的机票还没生成支付没有记录画出来就三个框两根线什么也说明不了。初学者常犯这个毛病因为“下单”听起来最像起点。快照的价值在于信息密度不在叙事顺序。3.3 逐条落地对象与属性槽位现在开始填对象。我给每个对象一个图内标识属性只填跟本图目的相关的其余省略。对象类型属性槽位节选p2001PassengerpassengerId P-88213name A 乘客phone 138****6621o1001OrderorderNo ORD-240518-0007createTime 2024-05-18 09:12:31status PAIDamount 1280.00t3001TicketticketNo TKT-0007-01fare 640.00status ISSUEDpassengerType ADULTt3002TicketticketNo TKT-0007-02fare 640.00status ISSUEDpassengerType ADULTpay4001PaymentpaymentNo PAY-240518-3311channel BANK_CARDamount 1280.00status SUCCESSf5001FlightflightNo XX1234departTime 2024-05-20 08:15status SCHEDULEDs6001SeatseatNo 12AcabinClass ECONOMYstatus OCCUPIEDs6002SeatseatNo 12BcabinClass ECONOMYstatus OCCUPIED这里有几个刻意的细节值得展开讲。金额对得上订单 1280.00两张机票各 640.00支付流水 1280.00。这三个数在三处出现快照里必须自洽。这是对象图最实用的一种校验——跨对象的算术一致性。类图永远表达不了这个只有快照能。座位状态是 OCCUPIED 而不是 AVAILABLE因为这张图对应的时刻票已经出了。如果有人把座位状态写成 AVAILABLE 而机票状态是 ISSUED这就是一条真实的数据 bug快照把它抓个正着。这种“状态联动”的检查在真实系统里靠人工核对极难发现画一张图反倒一目了然。证件号脱敏真实文档里展示个人信息要脱敏idNo 3101**********1234这种写法既保留了格式信息又不泄露内容。写技术文档的人容易忽略这一点但这是硬要求。时间格式统一createTime精确到秒departTime精确到分。为什么不统一因为业务上就是这么定义的——订单创建要审计精确到秒航班时刻对外发布精确到分就够。对象图如实反映这个差异比强行统一更有信息量。3.4 把链一条条连上并做一致性检查对象填完接下来是连链。连一条核一条别连完再核否则错了不好定位。序号源对象角色名目标对象对应类图关联多重性核对1p2001下单人o1001Passenger 1 — 0..* Order该乘客 1 个订单符合2o1001所含客票t3001Order 1 — 1..* Ticket订单 2 张票≥1 符合3o1001所含客票t3002同上同上4o1001支付流水pay4001Order 1 — 0..1 Payment1 条符合5t3001承运航班f5001Ticket * — 1 Flight每票 1 航班符合6t3002承运航班f5001同上同上7t3001占用座位s6001Ticket 1 — 1 Seat1 对 1符合8t3002占用座位s6002Ticket 1 — 1 Seat1 对 1符合9f5001出发机场ap7001Flight * — 2 Airport出发端 1 个符合10f5001到达机场ap7002同上到达端 1 个符合连完之后回头看这张图的形状以订单 o1001 为中心向上连乘客向下连两张票票再各连一个座位、共同连一个航班订单侧面挂一条支付。整体是个星型带两条尾巴的结构视觉上很干净没有交叉线。这不是巧合——对象图的布局应该跟着中心对象走。选一个“主角对象”放中间其余按关联层级往外铺读者的视线自然有落点。如果图里有多个同等重要的中心说明这张图想说的东西太多该拆成两张。还有一条链我故意没画航班 f5001 到机型、机型到座位布局这条线。不是不重要是这张图的主题是“订单与客票的实例关系”机型属于运力配置混进来会稀释重点。对象图要敢于省略省略不是遗漏是取舍。但省略要在文字里说明一句“本图未展开运力配置相关对象”否则读者会以为你忘了。3.5 三个典型快照变体用对比体现状态迁移一张快照只能说明一个时刻。想表达状态变化正确做法是画多张快照并排对比而不是在一张图上加箭头。我通常会准备三个变体变体一待支付。订单 o1001 的 status 是PENDING_PAYMENT机票对象还不存在或者存在但 status 是RESERVED支付链缺失或指向一条INIT状态的流水座位状态是HELD而不是OCCUPIED。这个快照用来回答“占座但未付款时数据长什么样”。变体二已出票就是上面那张。三个状态全部推进订单PAID、机票ISSUED、座位OCCUPIED、支付SUCCESS。这是主图。变体三已退票。订单PARTIALLY_REFUNDEDt3002 的 status 变成REFUNDEDs6002 回到AVAILABLE支付流水新增一条REFUNDED记录或者原流水状态改为PARTIAL_REFUND。注意 t3001 还在链也还在只是状态变了——链不会因为业务状态变化而断开除非关联本身失效。这一点很多人搞混以为退票了线就该删掉。线代表的是“曾经建立的关系”在很多业务里这个关系要留痕。并排三张图之后读者能直接看出哪些槽位变了、哪些链增减了、哪些约束在哪个状态下成立。这比一张图配三千字解释高效得多。这也是对象图在文档里最舒服的用法图负责呈现事实文字负责解释规则。4. 六个高频坑与排查路径4.1 把操作、流程、注释混进对象框第一类错误最常见对象框里写了pay()、cancelTicket()或者框里塞一句“此时用户正在支付”。前者的错误在于对象图不呈现操作后者的错误在于引入了时序语义。排查方法很机械逐字扫描每个对象框凡是动词短语一律划掉或移到文字说明里。属性槽位里只能出现名词和值。还有一种隐蔽的变体在属性槽位里写过程性描述比如status 等待第三方回调后更新。这看起来像值其实是注释。正确的是status PAID把“怎么变成 PAID 的”交给文字。4.2 链连错方向、连出不存在的关联第二类错误是链的合法性。表现有两种一是连了类图上没有的关联二是把单向导航画成了双向。第一种的根因通常是“业务上有关系”和“模型上有关联”被混为一谈。业务上乘客和机场当然有关系乘客从这个机场出发但如果没有建模“乘客—机场”这个关联就不能在对象图上连。这时候该怎么办两个选择要么回去补类图如果这个关系确实需要长期表达要么在文字里说明“该关系通过航班间接获得”。我倾向前者因为能被问出来的关系多半真的需要建模。第二种的排查靠对照类图的导航箭头。如果类图是Order → Ticket单向对象图上就不要在图里暗示机票能反查订单。有些工具不画箭头那就靠角色名和文字说明补足。4.3 多重性冲突数线就能发现的问题第三类错误是快照违反类图承诺的多重性。这个前面提过但值得单拎出来说因为它出现的频率高得离谱。典型场景类图写订单至少一张票快照里订单是空的。原因往往是画图的人选了“刚下单还没选票”的时刻而那个时刻业务上其实不该存在——真实系统会在下单时就锁定至少一张票。也就是说多重性冲突往往不是画错了是暴露了设计矛盾。这时候正确的动作不是改图迁就而是回头确认到底是类图的多重性写错了还是业务流程有漏洞。排查动作给每个关联端标注“本图数量”做一张对照表逐个打勾。表不用给别人看自己核一遍就行但一定要核凭眼睛扫是扫不出来的。4.4 一张图想装下整个系统第四类错误是信息过载。十几个对象、几十条链、交叉线铺满整页读者看三秒就放弃。根因是没定主题。对象图不是系统全景照是带主题的局部特写。定主题的方法是先写一句话比如“这张图说明一个已支付订单如何关联到两张客票及其座位”然后严格按这句话选对象不在句子里的一律不画。如果确实需要展示全局做法是分层一张概览图只画核心对象每个核心对象再挂一张局部图。别指望一张图解决所有问题。4.5 属性槽位写成“待定”“xxx”第五类错误是占位符泛滥。status xxx、amount 待定、createTime TBD这种图的价值接近于零。对象图的核心资产就是具体的值值一虚图就退化成了一张画得比较丑的类图。如果某个值暂时拿不到正确做法是要么脱敏后给一个符合格式的假值138****6621比待定强一万倍要么在文字里说明“该字段取值依赖外部系统本图以占位符表示”并且真的用一个统一的占位符比如?别用 xxx 这种随手写的。4.6 忘了检查跨对象约束第六类错误最隐蔽单条链都对合起来违规。比如同一个座位 s6001 被两张有效机票同时占用或者同一个订单下两张机票的航班号不一致或者支付金额与订单金额对不上。这类问题只能靠约束清单逐个核。我常用的五条检查项判据违规后果座位唯一性同一航班内一个座位只被一张有效票占用超售、值机冲突金额一致性订单金额 有效机票票价之和 成功支付金额对账不平航班一致性同一订单下的所有机票指向同一航班本示例的简化约定出票异常状态联动票据 ISSUED 时座位必须 OCCUPIED数据自相矛盾主体一致性机票的乘机人与订单的下单人存在明确关系代订场景需额外建模这张表直接可以拿去当评审 checklist比在会上临时想问题高效。5. 从对象图再往外延它还能干什么5.1 用对象图直接生成测试数据这是我最推荐的衍生用法。一张规范的对象图字段值和对象标识都齐全几乎可以直接翻译成测试 fixture。翻译规则很直白每个对象变成一行数据库记录或一个 JSON 节点属性槽位变成字段链变成外键或嵌套引用。上面那张图翻成数据就是一条乘客记录、一条订单、两条客票、一条支付、一个航班、两个座位。测试用例写“支付成功后订单状态应为 PAID”直接用这份数据跑不用再编。更进一步把三个快照变体连起来就是一条完整的状态迁移测试链路从待支付快照出发触发支付断言系统进入已出票快照再触发退票断言进入已退票快照。三个快照 三个断言点这是对象图最实在的工程价值。5.2 用对象图对齐接口字段与嵌套结构前后端联调最烦的不是字段名不一致是层级不一致。后端觉得订单里应该嵌机票前端觉得机票应该单独一个接口。对象图能把这个争论变成可视化的选择。看那张图订单和机票之间是一条链链的两端都是独立对象。这意味着在数据契约里它们可以是引用关系订单里存 ticketNo 列表也可以是嵌套关系订单里嵌 ticket 数组两种都能表达同一个事实选哪种取决于查询模式——如果前端每次都要连着票一起展示嵌套省一次请求如果票会被单独操作引用更灵活。把这张图摆在会议桌上讨论的焦点立刻从“我觉得”变成“这个链该怎么落地”效率差好几倍。5.3 与类图、时序图、状态图的分工最后把边界划清楚免得用错工具。类图回答“系统里有哪些类型、类型之间有什么结构性关系”它是设计蓝图不带具体值。对象图回答“某一刻这些类型的实例长什么样、怎么连”它是快照必须有具体值。时序图回答“一次交互中消息按什么顺序传递”它有时序。状态图回答“一个对象在生命周期里会经历哪些状态、如何迁移”它有状态机。选型口诀很简单问结构看类图问某一刻的数据看对象图问先后顺序看时序图问生命周期看状态图。四者不是替代关系是四种不同的提问方式对应四种不同的答案形式。我见过最省事的做法是把一张对象图当作时序图的注脚时序图讲清“支付成功后系统做了哪几步”对象图补一句“做完之后数据长这样”。两张图配在一起读者既知道过程也知道结果比任何一张单独拿出来都完整。6. 一些纸面上学不到的手上经验画对象图这件事工具和规范占三成剩下七成全靠手感。分享几条我踩过坑之后总结的。第一条先写文字再画框。我的习惯是先用一段话把快照描述清楚“此刻乘客 p2001 有一个已支付订单 o1001订单含两张已出票客票分别占用 12A 和 12B 座位承运航班是 XX1234支付流水一条。”这段话写完对象和链基本就齐了剩下的工作是排版。反过来先摆框再想关系十有八九会漏对象。第二条给每张图配一句“本图不表达什么”。这句话能挡掉八成的误读。比如“本图不表达退改流程”“本图未展开运力配置”“本图不涉及多乘客代订场景”。写起来一句话省下的解释时间可能是半小时。第三条对象标识用“类名缩写 序号”别用业务主键。用o1001而不是用订单号当标识好处是改数据的时候不用全局替换。业务主键放在属性槽位里两个职责分开。第四条属性值尽量真实到能被程序直接吃。时间用标准格式金额带两位小数枚举值用大写加下划线。你写得越像真实报文别人拿去用的成本越低。这条听着像强迫症实际用起来回报极高。第五条别怕图小。一张只有五个对象、四条链的图只要主题明确、值具体、约束自洽价值远高于一张二十个对象的大图。对象图不是规模竞赛是清晰度竞赛。最后再分享一个我常用的自查动作画完之后把图遮住只读文字说明看能不能把图复原出来再反过来遮住文字只看图看能不能说出这张图想表达什么。两个方向都通这张图就立住了有一个方向不通说明图和文字有一边在偷懒。这个动作花不了一分钟但能挡掉绝大多数返工。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →