仓库源文

.. Kenneth Lee 版权所有 2024

:Authors: Kenneth Lee :Version: 1.0 :Date: 2024-03-15 :Status: Released

不要把细节特征当作设计


我观察到很多设计新手会把代码的细节特征当作设计。这种情况是这样发生的:新手刚刚 拿到一个需求,他其实并没有把握这个需求要怎么实现,他只是大概有一些方向性的选择。 但对于其他的限制,他可能并不那么确定。比如他可能并不肯定要用的的那些库是否能提 供足够的API让他完成功能,也不见得知道那个库的线程模型是否肯定和他想象一样……所 以,他并没有马上写出比如“API肯定是如何如何的”这种断言的信心。

于是,他就会先写一些代码,顺着那个“大致的方向”验证一下,会试用一下那些API,把 没有信心的的特性都试用一下,等等。等这些代码写出来了,他们就开始有信心了,于是, 他们开始仿照别人的“设计案例”或者“设计模板”,把这个验证代码的API,目录结构,对 象关系之类的东西,写成一份或者多份文档,声称:“这是我的设计”。

但这些不是设计。设计是用来控制细节的,不是用来被细节控制的。这就好比我们规划从 深圳出差去北京,那么考虑坐飞机还是高铁,打的去机场还是坐公交去机场,行李中带几 件换洗的衣服,这些都是设计。但你提前走了一趟,说过海德大道的时候要等17秒,拉行 李的时候要用左手,要在天虹门口等的士来,这些确实也是做这件事情的特征……但这些显 然不是设计。

这个问题放在一些具体的生活上非常荒诞,但在设计上,就不见得好分辨了。比如我说, 某个模块的对象创建API,接口必须是:::

Object create(int n_object, float init_value, ErrorReporter e);

你能看出这是细节的特征还是规划(或者说设计)的特征吗?

这两者的核心区别就是这个特征是否是被上层需求驱动的。行李中带几件衣服,这是被 “要出差几天”这个高层逻辑所驱动的,所以它可以指导你“找什么衣服,决定用多大的行 李箱……”这些细节的设计,所以“行李中带三套衣服”这是一个高层设计,但“其中一套衣服 是红色的”就不是设计,它只是刚好在细节上你有一套红色的衣服而已,它不是被高层逻 辑所驱动的。

但这不是定式,如果高层逻辑中有“客户喜欢红色”这个条件,那么“带一套红色衣服”就是 设计了。设计必须具体问题具体分析,不能套某种固定的模板。

就上面说的create API来说,这个名字是特征,还是它的返回值是特征,还是包含的参数 是特征,还是参数的类型是特征?我们不知道,我们要找这个特征的目的才能判断。

比如可以认为数据关系是特征,这个接口我们这样设计:

我们通过::

Objects create(n_object, init_value, ...);

创建对象,对象中可以包含多个成员,每个成员都各自的初值,这样我们就可以立即把成 组的成员立即(有初值了)投入运行。这个设计中,我们有确定的目标,我们关心我们需 要给定成员的数量和初值,返回的是一个对象的集合。

这个设计满足了目标,同时限定了细节:比如必须有n_object,init_value这两个参数, 而类型,其他参数以及报错方式,都可以在细节设计的时候根据具体情况再选择。

这样我们就有了一个很好的判断标准,决定什么是设计了:当你设定的约束,来自目标和 “绝对真理”的组合,用于约束进一步的设计,那么,它就是设计。否则,它可能仅仅就是 “细节的特征”。

.. note::

说起来,这个判断标准已经比较具有确定性了,但使用者还是要清楚这里的难度。因为 人类语言本来就具有不确定性的,就好比之前我们反复说的维特根斯坦的理论,我们传 递信息的时候总有Can be said clearly和Must be passed over in insilence的部分。 无论你写得再细致,你总会包含一些基于双方的经验,只能“你知我知”的部分。好比上 面的描述,我们默认认为这个create函数,换一个create_objects的名字没有什么问题。 这并不“严密”,但我们必须有这样的容忍,最多是到我们意识到双方的理解有冲突的时 候再去澄清它。

设计总是让人难受的。因为它反直觉,并且和后续工作存在天然的利益冲突。比如在前面 的例子中,我们要求create必须成组提供对象,这可能是被高层设计的某个要求决定的。 如果实现成单个的对象,可能更容易,性能也更高。但即使如此,也没有用,因为我们就 要成组提供对象。这个事情,不干就偏离目标了,干了,下层设计者不爽,从而导致要求 执行这个约束的上层设计者也得罪人。但要对着目标去,这就是必须的。这个道理就好像 我们都不想起床去上班,但理智总是和“执行的快乐程度”是逆行的。如果没有这个“必须 赚钱养家”的需求驱动下的“必须准时上班”的设计,我们不会起床去上班。这是设计这个 动作的目的(让我们理智地执行细节)。我们就是不能“宠着”我们的执行。

所以,设计必然是一件不讨好的事情。不要期望设计会让下层设计者或者实现者高兴。甚 至对于设计者和实现者都是你自己的情况下,不要指望自己的设计会让自己开发的时候更 高兴。

所以,如果你设计了一组约束,这组约束你不说,细节设计或者实现的时候也会这么执行, 那么这个设计肯定就失败了,因为有没有这个设计,结果都是一样的。

最后看一个例子。最近评审了一个指令集编址空间安排的设计。作者把空间分成了多个分 区,分别用于常用指令,加速器指令和向量指令。然后巧妙地安排了几个位置的bit在不 同的情形下可以判断指令所处的空间。这个东西我就认为不是设计。因为找不到它的设计 目标:我们当然需要对指令集空间进行规划安排,但为什么需要分成三个分区?这个是被 什么目标驱动的?这里没有。这里直接出结论:分了三个空间。所以这个达成了什么收益? 没有这个收益来驱动(遇到障碍必须坚持,否则就会失去这个利益),后面具体设计的时 候空间不够了,你会不会修改?一旦修改,我其他基于这个“三个分区”的设计是不是都需 要推翻重新来?

所以你看,设计是有利益驱动的,它的投入是能赚钱的,我们花这个时间不是为了完成某 种仪式,而是为了把这些利益抠出来,然后保证我们保证这个约束得以满足的所有条件, 都被用作下一层的设计的约束。如果设计没有起到这个作用,设计失败了。

所以,我们并不反对为了验证某些结论,提前进行编码,但验证是验证,验证的代码并不 是设计,设计还是需要设计的。