手记

技术配图的一些心得

引言

写过不少技术文章,以及给不少技术思路手绘示例配图之后,在这方面有了一些心得,本文给出一些关于配图的见解,仅供大家参考。

关于技术配图

对于理工科出身的同学,对于可以量化的事情,总是很习惯根据量化差异来做出判断,比如一个程序性能优化之后,对比优化之前快出多少,都能很容易的通过一个量化的数字来说明。

但是对于那些不能量化的东西,就很难说出具体好在哪里了。

本文主题要讨论的“技术配图”就属于这种很难量化的领域,很难有一个标准来量化说明两幅图之间差别在哪里。我也是画了很多图,以及看了别人的很多配图之后,才慢慢有一些心得,本文权当个人的一些的总结,抛砖引玉,欢迎交流探讨。

本文并不是一个画图工具的对比说明,尽管现在各种绘图工具已经很多,也各有自己的优缺点,但是在这里并不讨论具体工具的使用,会把更多的文字放在配图的一些注意事项上。但是,也总有人问我文章的配图是用什么工具做的,在这里再回答一次:OmniGraffle,一款目前仅有 Mac 版本的工具软件。

配图的重要性

在开始交代具体的配图注意事项之前,有必要先说说配图的重要性。

绘图,某种程度来说也是辅助自己思考某个技术点的手段之一,以我个人的体会来说,有时候讲不清楚一个技术点的时候,就手绘图出来,比朴素的文字更容易说明问题。其中的原因,有可能是:图片可以有多维的信息,而文字通常只有一维,遇到文字表达能力不太好的人,这仅有的一维能力可能还不好发挥出来。

所以,在交代技术细节、沟通交流的时候,尽量多画图。反向的,图画多了,也自然慢慢会找到感觉,更好的通过图示表达思路。

顺便一提,还有比朴素的文字表达更差的技术沟通方式,就是简单粗暴的贴一大段代码上去。这种做法,其实更多时候是对作者的思路没有太多个人的整理,纯粹偷懒的方式,往往最后回头再看写过的文字,可能连自己都看不懂了。

如果产出某些内容的时候,能假设自己未来就是这些输出的读者、维护者,那么输出起来会更完善一些。比如写的代码、文章、甚至于提交代码时候的信息,如果能考虑是写给未来的自己看的,会更清晰、尽可能留下更多的信息。我最开始要在文章里大量配图,也是为了将来自己回看的时候能看懂。

总之,尽可能多画图来表达技术思路。

下面开始正题,以下会以简单的几个原则及示例来说明。

区分、联系、组合

配图中,应该尽量将不同的模块、组件等区分开来,“区分”的方式有很多,常见的有:

  • 使用不同的颜色。
  • 使用不同的形状。
  • 使用箭头、曲线等表示数据的走向、趋势。

等等,所有的这些手段,概括起来就是尽量在图中,将不同的元素区分开来,“有区分”意味着至少有一个维度的不同,这样能给读者更加清晰的观感。可以结合下面的例子来理解区分、联系和组合的绘图表达。

分组

一个模块里,可能由多个组件构成,可以把这些组件分组到一个更大的模块中。

分组是非常常见的一种手段,这里多举几个例子。

上图中,每个 CPU Core 中有 L1、L2 缓存,于是把这些组件合并在一起放在 Core 组件中,周围使用一个正方形包裹起来,同时这个正方形左上角有一个 Core 的说明文字,这样一目了然:Core 模块,由 L1、L2 缓存构成。

上图出自 Raft 论文,整体上划分为了 Client、Server 这两大部分。而每个 Server 又有以下三部分组成:

  • 一致性算法模块。
  • 状态机。
  • 持久化的日志。

所以,图示中将这三部分合在一起放在同一个矩形里,表示一个Server有这三个组件。

另外还需注意的是,一般这种分组中外围的矩形,有这样的讲究:

  • 一般使用斜面矩形,即四个角是圆角的矩形,这样圆润一些的边角看起来会更舒服一些,如上图。
  • 如果这个组合,是一种逻辑上的组合,那么线的形状一般用虚线;否则就一般用的实线。

在分组时,有时候可以将相同类型的模块层叠起来,这样会更加简洁,如下图:

上图是出自 Raft 论文中的状态机模型,其中想要表达的一个点是:

  • 有多个 client 向 server 发起请求。
  • server 要达成一致,需要将日志在 server 之间同步。

但是上图中,并没有把这些同类型的组件分开表达,而是巧妙的使用层叠的方式,简洁得表达了有多个 client、多个 server 的情况。

趋势

下图是描述不同层次存储的访问速度,于是用了两个方式来表达访问速度的变化趋势:

  • 左边的箭头表达速度和成本的变化。
  • 不同大小的多边形表达了这些存储空间的变化:越往上访问速度越快,但是对应的存储空间也更小。

再比如,下图中,是说明 sqlite 中 btree 页面的数据组织的。其中的两部分内容,Cell 地址数组以及 Cell 内容区为变长大小,前者从地址低位向高位生长,后者反之,于是在图中,就用箭头示例出地址的高低位区别,以及两者的增长方向:

(出自 sqlite3.36版本 btree实现(五)- Btree的实现 - codedump的网络日志[1])

联系

这在涉及:

  • 状态切换。
  • 数据流向。

等场景下是非常常见的手段,比如经典的 TCP 状态机切换:

以及 TCP 三次握手流程,也是典型的“状态切换”:

需要说明的是,以上的图示中:

  • 箭头代表的状态切换走向中,同时也配以文字说明是什么动作导致的状态切换,这样这个图示就更清晰了。
  • 箭头也分为实线和虚线,一般而言,虚线表示数据的走向,实线表示状态的走向。

禁止

需要禁止或者错误的行为,可以用特殊的符号,如带颜色的“×”符号示意出来;反之,可以用带颜色的“√”符号示意出来,而且表示禁止的时候,一般用红色会更显眼,下图就是一个示例:

(出自 Memory Barriers in .NET · Nadeem Afana’s Blog[2])

说明

如果不好说明问题,可以在图示中搭配简短的说明文字。注意:这类型文字一定要足够的简短,否则可能会喧宾夺主。

比如下图中,有两部分蓝色注解的文字来说明不同的表类型:

(出自 sqlite3.36版本 btree实现(五)- Btree的实现 - codedump的网络日志[3])

再比如下图中,使用注解文字来说明查找数据的两步流程:

(出自 sqlite3.36版本 btree实现(五)- Btree的实现 - codedump的网络日志[4])

分类

有时候需要使用类似“{”这样的符号,对一类元素做一些说明,例如:

下图中,是说明 sqlite 中 btree 页面的数据组织的,最右边的以“{”包起来的文字,对每部分做了简要的说明。

(出自 sqlite3.36版本 btree实现(五)- Btree的实现 - codedump的网络日志[5])

下图中,将页面划分为不同的部分,这些不同的组成部分,既使用了颜色进行区分,也使用了向下的“{”辅以文字说明。

(出自 sqlite3.36版本 btree实现(五)- Btree的实现 - codedump的网络日志[6])

步骤

如果配图是需要讲解某个操作的步骤的,可以配以数字来辅助理解整个流程。

下图中,表达的是根据帧数查找页面编号的两个步骤:

(出自 sqlite3.36版本 btree实现(四)- WAL的实现 - codedump的网络日志[7])

下图中的步骤就更多了,并没有显得很乱,大概原因在于:

  • 最左边表达了每一步的步骤。
  • 每一步写入数据之后,显示 WAL 文件在写入之后的内容。
  • 最右边使用“{”表达修改之后的数据。

(出自 sqlite3.36版本 btree实现(四)- WAL的实现 - codedump的网络日志[8])

展开

如下图中,是用于展示 wal index 索引文件格式的:

  • 左边示例每部分内容的大小,想说明的是,那个索引块大小为 32 KB,而第一块的头 136 字节为索引文件头。
  • 于是,在右边图中,将左边不同模块的具体格式继续展开说明。

(出自 sqlite3.36版本 btree实现(四)- WAL的实现 - codedump的网络日志[9])

总结

以上简单总结了一下个人技术配图的一些心得,总的大原则是:

  • 区分:将组件、流程、趋势等之间的“区分”尽可能在图示中通过各种手段(如不同的颜色、形状、箭头)表达出来。
  • 联系:组件之间的数据流动、状态切换等,都是它们之间的联系,也需要通过各种手段表达出来。
  • 说明:可能的话,要在图中加上一些说明文字,如步骤说明、分类说明,等等。
0人推荐
随时随地看视频
慕课网APP