摘要:时序图是统一模型语言UML(Unified Model Language)中一种用来表示实体间交互关系的图。
1 前言
在定义系统间接口或模块间接口时,时序图使用起来非常方便,工作中经常涉及要与第三方系统协商定义接口,或者定义系统内多模块间接口的情况,经常会看到很多时序图。有的时序图画的很漂亮,很好的帮助读者准确理解业务和实现方法,而有的时序图则读起来人云山雾罩,痛苦不已。本文不打算再说一遍时序图的结构和步骤,只想说明时序图中经常遇到主要问题,并许下一个美好的愿望:希望以后的工作中再也不要遇到难读的时序图。
时序图是统一模型语言UML(Unified Model Language)中一种用来表示实体间交互关系的图,英文Sequence Diagram,有的人把它称为序列图、顺序图、循序图,个人习惯于称为时序图,下文都以这个名字来称呼。
画时序图的工具非常多,从早期的Rational Rose、Sybase Power Designer、Visio,到Enterprise Architect、StarUML,甚至用Typora来画Markdown风格时序图也不错,再到现在按公司的要求换用亿图图示Edraw,这些工具都不错,稍微适应下就能画出漂亮的时序图了。
值得推荐的是,公司CloudDesign在线设计工具提供的CloudModeling绘图工具用起来也相当舒服,既便于在团队分享,又提供了Word插件便于导入设计文档,如果修改了时序图也只需要在word文档中更新一下就自动刷新了,非常方便,强烈推荐使用。网址:https://clouddragon.huawei.com/uadp/home
下面进入正题。
2 关键点1:必须明确上下文
掌握了这一点就成功了一大半,没有做到这一点就基本画不清楚了。
为什么这句话说这么狠?不就画个时序图嘛,关上下文什么事?因为看过太多让人痛恨的时序图都栽在这个问题上。
我们知道时序图中参与交互的实体只有两类,即角色(Actor)和对象(Object),如果连交互的实体都不能明确的定义和达成一致,忽东忽西,忽大忽小,具体交互的流程怎样可能说清楚,使所有读者和写作者达成一致呢。
为说明这个问题,以车联网的场景举个例子,比如远程控制特性的交互时序图。
车辆授权交互时序图
远程开车窗交互时序图
远程开车门交互时序图
如图所示,我们看到交互实体中出现了多个类似但又不同的表述,例如“车主”、“被授权用户”、“被分享用户”这一组,“手机App”和“车主App”这一组,“TSP平台”、“TSP系统”和“车云”这一组,而在车辆方面,有时称为“车辆”这么大的粒度,有时又称细分为“TBox”、“车身控制模块”、“PEPS”。
这里仅仅举例了3个交互时序图,而一个复杂系统往往会出现几十上百个,当每个时序图的作者都随心所欲的对交互实体进行命名时就会出现极其混乱的局面,最终貌似每个时序图都看起来很有道理,放在一起看却难以准确理解,使读者抓狂。
解决办法:很简单,画出一个上下文图,把所有时序图中涉及的交互实体都放进去,规定它们的名字,要求所有时序图中的实体必须与上下文图中保持一致,不得自己定义新的。如果确实需要增加新的实体,那么首先更新上下文,在上下文图中把实体定义进去才能使用。
例如针对上述车联网的场景,增加一个这样的上下文就可以更加清晰:
在实际项目中,可以利用工具来实现这个一致性。例如CloudModeling绘图工具中,我们会定义完整的系统上下文和系统逻辑架构视图,要求所有的交互时序图必须从这里面链接引用角色和,而不是自己新建一个。
3 关键点2:决定该不该把某个实体放进时序图
在上面的例子中,在车辆相关实体中,有时称为“车辆”这么大的粒度,有时又称细分为“TBox”、“车身控制模块”、“PEPS”。事实上,“TBox”、“车身控制模块”、“PEPS”都是车辆内部模块的一部分,那么究竟什么情况下该把“车辆”这么大的粒度放入时序图,什么情况下该把“TBox”、“车身控制模块”、“PEPS”这样的内部模块展示出来呢?
个人理解是这样的:实体是否展示与业务场景和所设计的对象密切关联,只有在业务场景中与所设计对象有直接交互的实体才有必要放入时序图中,间接交互实体则应当去掉。
在上面的例子中,如果我们设计的对象是TSP及车主手机App,那么车辆内部的实体部分就不需要展开,只需要展示与车云直接交互的TBox模块即可,如下:
远程开车门交互时序图
但是,如果我们设计的对象换成了车身控制模块,那么交互的实体就应当省略TSP及车主手机App相关的实体,把关注点调整到与车身控制模块直接交互的实体上来,例如:
远程开车门交互时序图
4 关键点3:响应消息要与请求消息分开
时序图中交互实体间水平的线条用来表示消息,最常见的有三种:
4.1 同步消息(Synchronous Message)与返回消息(Return Message)
同步消息(也称为调用消息)一定要与返回消息成对使用,特别要强调的是:返回消息样式不得使用同步消息的样式,这是两个完全不同的事情。同步消息表示一个实体对另一个实体的一个接口调用,被调用方要按流程实现提供接口的编码,并按返回消息内容要求进行返回;调用方需要按流程实现调用接口的编码,并对返回消息内容进行处理。为了更清楚的说明问题,往往会在消息中注明关键的参数。
经常看到的错误是不区分同步消息和返回消息,乱画一气,非常让人恼火。有时会看到像这样的时序图,特别注意其中红色的消息线条,看起来似乎“TBox”实体对“TSP”实体的一个接口调用,但实际问一问作者,发现并不是这样,而是上面消息的返回消息。这样的画法就给人造成一种错觉,以为交互实体双方需要实现一个新的接口。
4.2 异步消息(Asynchronous Message)
消息发送者通过消息把信息发给消息接收者,然后继续自己的活动,不等待接收者返回消息。
4.3 自关联消息(Self-Message)
表示实体自身需要实现一个处理过程,也可以调用一个外部实体的消息。
同样以上面车联网场景为例,假定设计的对象是TSP及车主手机App,那么我们只能对这两个实体分解开发任务,如图,我们要求“车主手机App”实现“提供开车门功能”,具体包含向TSP的请求开车门消息调用及返回结果的处理;“TSP”要实现“提供开车门接口”,具体包含向TBox的下发开车门指令及返回结果的处理,还包含一个发送短信通知的异步消息,TSP提供的开车门接口请求参数中应包含关键的用户token、车辆ID信息,返回结果中应包含关键的成功/失败、错误信息。
注意:这里引入了一个新的实体“短信中心”,也应当在上下文图中先加进去才能使用。
远程开车门交互时序图
5 总结
三个关键点:所有交互实体放进上下文,不直接交互的实体去掉,响应消息要与请求消息分开。如果你画的时序图确保以上三个关键点都做到了,我想至少拿出去给大家看的时候会少挨一点抱怨。