仓库源文

.. Kenneth Lee 版权所有 2018-2020

:Authors: Kenneth Lee :Version: 1.0

设计规范


这篇可能要得罪人……

最近和几个不同的开发团队交流,有做软件的有做芯片的,都涉及到设计规范的问题。我 个人有个感觉:“对设计规范的态度,很大程度上反映了这个组织或者架构师是否有足够的 设计经验”。但还是那句话,“唯我道大故不肖”,我把这个观点表述出来,如果被一个群体 接受了,他们就会做出“不是这样的样子”,这个特征就不能被用于观察了。所以读者要想 解决问题,知道我什么意思就好了,你要把这个作为“原则”,用得不对不要来找我;)

很多人认为设计规范“非常重要”,有两个常见的论据:

  1. 很多成功的公司,团队,项目都有编程规范,说明编程规范是一种成功的实践

  2. 如果团队里都是高手,当然用不上设计规范,但我们大部分都是低手啊(规范提供了基 本的质量保证)

我觉得能提出这两个论点的,如果还是架构师,这些人就可以辞退了,基本上都是神棍。

我们一个个看。

成功的公司表现出来一个特征,和这个特征驱动了这个公司成功,这两者没有相关性,根 本不能用作选择这样做的证据。举出这个证据,说明这个架构师一点逻辑思维能力都没有 ,这种人给团队作技术引路?还不如找个萨满或者程序员鼓励师之类的更靠谱。

这个问题我后面进一步细化,现在我们先讨论第二个问题。

我练二胡练了十几年了,三天打鱼两天晒网的,练得不好,但肯定比刚练一年半载的普通 玩家好,等以后编程序不好混了,去街头拉个《江河水》、《蓝花花》什么的卖惨还是够 的。好了,你现在告诉我,你让练了3个月的人也能拉《江河水》,设定什么样的《二胡演 奏规范》能够提供帮助?或者说,你要让我拉出演奏家的水平,又设定什么样的《演奏规 范》可以达成?

二胡技术是一遍遍的练习中提高的,调式,音阶,琶音,短弓,长弓,抖弓,跳弓,滑揉 ,滚揉,压揉……这些每个动作都是要单独练习,重复几万次才培养出来的呀。最后你看到 的一些特征,那只是它的表面特征而已啊。那些一个个单独的细节才是支撑这些特征的根 本啊,学特征并不能取代这些细节的呀。

这不是显而易见的事情吗?

写一段程序,重复到什么程度需要抽象成函数,宏,模板,又复杂到什么成什么程度必须 分解为两个实现,这是要打磨才会知道的啊。一个函数怎么才能不成为多线程程序的瓶颈 ,使用不同类型的锁会制造什么样的瓶颈模式,你跟我说你看一本《编程指导》就会明白 ?不带这样看不起我们程序员的专业技能的吧?

你去找一个二胡老师,说“我也不想练习,你给我本秘籍,你让我回家对着拉个《新婚别》 行不?”,二胡老师会觉得你神经病(反正我现在的水平让老师教这个曲子,他说“你还是 不要毁名曲吧”)。怎么你就会觉得给“低水平”程序员一本《规范》,他就会写出好程序?

你不要跟我说,虽然不能完全解决问题,但也有一点用吧?——这句话你跟音乐老师说去, 我估计大部分音乐老师会跟你说:把你那本《规范》给我扔一边去,给老子每天拿出8个小 时出来练琴。

《设计规范》、《编程规范》是给高手(或者reasonable的程序员)提供帮助,不是教菜 鸟写程序,你不会写程序,好好学写程序。不是学《设计规范》。Linux Kernel上传的时 候都要求checkpatch.pl,那是在你逻辑已经搭得很Solid之后,帮你打磨一下细节,不是 让你一开始就对着这些细节来写程序。不要搞反了。

那么设计规范到底是什么东西呢?其实设计规范和库,模块,函数本质上没有任何区别, 都是一种“抽象设计”。我们先看看抽象是什么,比如你要做两个函数,分别计算算术平均 和平方平均:::

    def arithmetic_mean(a):
      return sum(a)/len(a)
    def quadratic_mean(a):
      return square_root(sum(square(a))/len(a)) 

这背后有一个自然语义是重复的,就是“把一个数组的内容加起来,然后除以数组的长度。 这就形成这个抽象:::

    def mean(a):
      return sum(a)/len(a);
    def arithmetic_mean(a):
      return mean(a);
    def quadratic_mean(a):
      return square_root(mean(square(a))) 

这里的mean就叫“抽象”,它不具有算术平均和平方平均的其他考量,要拿到一个转义过的 逻辑:“进来的是数组,我要把数组中的成员加起来,并除以它们的数量”,其他东西我一 概不知。这个“一概不知”,是你抽象的基础。

其实我们应该早就注意到了,这个例子中的mean和arithmetic_mean的语义是一样的,所以 ,这个抽象其实是个样子货,正确的抽象应该是这样的:::

    def arithmetic_mean(a):
      return sum(a)/len(a);
    def quadratic_mean(a):
      return square_root(arithmetic_mean(square(a))) 

但真得一定如此吗?你以为你看到的代码就是全部?后面升级这个系统说不定是这样的:::

    def mean(a):
      return sum(a)/len(a);
    def arithmetic_mean(a):
      log(arithmetic, a);
      return mean(a);
    def quadratic_mean(a):
      log(quadratic, a);
      return square_root(mean(square(a))) 

呵呵,你原来的抽象还是得改回来。否则你就会变成这样的逻辑:::

    def arithmetic_mean(a):
      if is_quadraic(a):
        log(quadratic, a);
      else 
        log(arithmetic, a);
      return sum(a)/len(a);
    def quadratic_mean(a):
      return square_root(arithmetic_mean(square(a))) 

现在明白我前一篇(再谈“法自然”的设计思路)说的“关联是需求决定的,不是设计决定的 ”的意思了吧?

所谓抽象,本质是除重和简化逻辑空间,它是对现实的“总结”,不是对现实的“改变”,这 就是“法自然”。是自然在驱动你,不是你在驱动自然。

回到设计规范这个问题,你决定做出一个“抽象”,比如“所有函数必须用动宾结构的语法形 式”,这在解决什么问题呢?这是在解决“沟通”的问题,当我们看一个标识的时候,一看见 它是个名词,我就知道它99%的可能是个变量,看到一个标识是动词,我知道它大概率是个 函数。这降低了我们理解代码时的复杂度。

又比如说,我们禁止C语言中使用setjmp和goto,这又带来另一个抽象:任何一段代码,只 要里面没有break, return,必然在这段代码的末尾退出(大概这样吧,严格定义要写的东 西太多,冲淡我的表达了)。

《规范》只是另一种形式的抽象,和基于库,DSL,模型的抽象没有区别,重点不在于这个 形式,而是你抽象了什么。而越高层的抽象就越难,而且越趋向无用。比如你把加减乘除 抽象为ArithmeticOp(a, b),这个抽象有啥用?只是把“区分”放到这个“代理”的内部而已 。

但如果你是做一个模拟器,每个Op都要进行执行时延的统计,这个抽象就有用了,因为它 拿到了一个“共性”,并对这个“共性”进行了“利用”。这个共性就是,“我不知道这是什么计 算,但无论它是什么计算,它都表现为一个执行时间”,所以我可以利用这个抽象“统计整 个执行的总时间”。每个抽象能在系统中发挥作用,它必然做了一件只有它能做,别人不能 做的事情,否则这个抽象就会变成纯粹的代理,这种纯粹的代理会恶化整个逻辑链的健康 。

所以,你能不能做出抽象,取决于你能否找到这个减少接口语义复杂度的模式。如果你找 不出来,《规范》不是“有病治病,无病防病”的“补品”,是药三分毒,用面粉当保健品卖 算是良心企业了,真给你药,吃死你。

所以,很多挺有经验的工程师被人推上“提高组织开发效率”这种位置上后,首先想到的是“ 设计规范”。我总忍不住提醒一句,“你们何德何能进行这种层面的抽象啊?”

我不是看不起你,而是所有的抽象,都基于对全集的有效观察和抽象。你跳过这些观察, 就想直接设定抽象,这个事情本身就是在赌。我不反对你赌,但你赌得连自己在赌都不知 道,你就明白为什么那么多人输得倾家荡产了。

还有人会有这种主意:我可以出一个规范,违反规范的人我也不反对,他们可以到我这里 要和我讨论了,改进我这个规范以后我们这个规范就可以完善了。嗯,对。这种方法确实 是一种“构建,发现”Pattern的手段,但我还是要问那句话:你要问自己何德何能可以做这 种控制。通常如果只有一两个产品,不少高工是“有德有能”可以这样控制的,但如果是一 个有几百个产品的组织,那就完全是另一个故事了。这完全是一个人如何和复杂的世界对 抗的问题,你觉得你可以搞清楚整个世界的所有信息,这个事情战略上就可以判断是失败 的。

其实,我个人的经验是,如果你真要提高整体开发水平,你确实水平高,你可以解决一个 模块的问题,影响一拨人,然后让其他模块复制这个成功经验……如此类推,当这个滚动过 程大到一定程度,后面的事情都不用你管了。如果你仅仅想让一堆低水平的工程师衬托你 的“高水平”……嗯,那当我没有说过……