Keep It Simple, Stuipid!

此书写于2003年,里边有一些东西是过时的。
比如版本控制系统一节,并没有提到git(2005年才发明),同样,文档格式也没有markdown的内容。

而有些内容,比如说到vi和emacs,或说到Windows时,有些过于有偏向性了,就像译者序里说的像个“美国愤青”。

不过抛开这部分内容,关于程序设计方面的讨论,确实是让人受益颇多。

在第一章,书中提到了以下一些原则:

模块原则:使用简洁的接口拼合简单的部件
组合原则:设计时考虑拼接组合
简洁原则:设计要简洁,复杂度能低则低
吝啬原则:除非却无它法,不要编写庞大的程序

清晰原则:清晰胜于机巧
分离原则:策略同机制分离,接口同引擎分离
表示原则:把知识叠入数据以求逻辑质朴而健壮
生成原则:避免手工hack,尽量编写程序去生成程序

通俗原则:接口设计避免标新立异
透明性原则:设计要可见,以便审查和调试
缄默原则:如果一个程序没什么好说的,就保持沉默
补救原则:出现异常时,马上退出并给出足够错误信息

优化原则:雕琢前先得有原型,跑之前先学会走
多样原则:绝不相信所谓“不二法门”的断言(指软件作者无法预想到软件的所有用途)

健壮原则:健壮源于透明和简洁
经济原则:宁花机器一分,不花程序员一秒
扩展原则:设计着眼于未来,未来总比预想快

简单的分分类:

模块、组合、简洁、吝啬,讲的是控制程序复杂度,就是我们现在说的模块化,高内聚低耦合;

清晰原则,指出代码更是给人看的,所以清晰的代码更易于维护和减少bug;
分离原则,指的就是表驱动法;
表示原则,指出了数据比编程逻辑更加清晰,同样指向了分离数据;
生成原则,指用代码生成完成重复劳动;

通俗、透明性,指向程序接口设计;
缄默、补救,指向程序行为设计;

优化、多样,说出了那句名言:过早优化是万恶之源。

健壮、经济、扩展,与程序设计时的理念相关。

模块化

软件设计的两种方式:一是明显没有bug,而是没有明显的bug。

API作为模块之间的滞塞点(choke point),隔离了模块内部的细节,同时API也定义了整个体系(而不是具体实现代码)。

API的设计需要考虑两个特性:紧凑性和正交性。
紧凑指一个设计能否装进人脑,一眼能够看懂。(不一定小巧)
正交指无副作用。

SPOT原则(single point of truth);DRY原则(don’t repeat yourself);
缓存可能是种过早优化,而且缓存是bug的温床;
大量重复的代码,也许可以通过更高级的表现形式生成。

甚用全局变量;
每写一个函数原型,立即增加一行注释(可以用一句话描述函数功能及约定);
参数/成员的最佳个数:7个以内;模块的最佳行数:200到400行以内。

文本化

这一章讲了文本化协议的优点,互用、透明、可扩展。

除非需要紧凑及性能,否则文本化永远是个好选择。

后边列举了一些常见的文本化格式:

DSV风格

Delimiter-Separated Values

如Unix口令文件(/etc/passwd文件)以冒号分隔;或CSV逗号分隔。

注意需要使用转义符号支持分隔符本身和换行;再就是使用空格而不是制表符(因为视觉上无法辨别)。

作者这里抨击了Windows的CSV格式,CSV使用双引号而不是转义来处理字段值包含分隔符的情况,引入了过多的特殊情况,造成了格式和程序的复杂度上升。

XML

这个不多说了,结论是XML既可能简化问题,也可能使问题复杂化。

INI格式

两层组织结构更适合INI格式(section是第一层)。

作者这里还不知道的是,git的配置文件就是ini格式的(哈哈)。

JSON

因为书是03年的,作者并没有提到JSON格式。这里我加上,是因为JSON格式现在太重要了,作为数据存储和通信协议都很不错。

注意事项

后边作者讲了一些文本化格式约定俗成的内容,如用井号#注释,使用反斜杠\转义,忽略LF和CRLF的区别,忽略空格和制表符的区别,包含一个版本号,注意浮点数取整的问题等。

通信协议

书中主要例举了SMTP、POP3、IMAP这些邮件协议,以及HTTP协议。

邮件协议给我的启示是,在给机器读的状态码之后加入给人读的文本描述,让调试更容易。

不过邮件协议应该是有状态的,作为对比,HTTP是无状态的。

最后作者也讲到了XML-RPC、SOAP这样的RPC协议。

容我最后再提到谷歌的protobuf和gRPC,让这一节更加完整。

透明性

透明性/可显性,就是将软件的行为展示出来,降低使用时的心智负担。

作者例举的是音频编辑软件的波形图,大多数软件都有的–verbose选项,以及gcc可以将每个阶段的结果都输出出来。

编码时也要注意透明性,如调用层次超过4层就要当心,注意API的正交性,少用magic number等。

在做GUI设计时,也要暴露出一定的内部细节,避免过度保护。

接口设计

这里的接口指和用户打交道的接口,如GUI、CLI等。

CLI设计时,注意数据要宽进严出,以方便其他程序将这些输出作为输入。不需要的信息不丢弃。以及不增加无用的输出(如将1.5写成150%)。

配置

Unix中的配置查找顺序:

  • /etc下的配置文件(即全局配置)
  • 系统环境变量
  • 用户主目录下的配置文件
  • 用户环境变量
  • 命令行参数

以上按照顺序查找,一个例外是,通过命令行指定配置文件路径。

书中也提到了常见的命令行单字母参数,如-l表示list,-a表示all,这里不再多说。设计时符合通俗原则,不要标新立异即可。

更多

书中内容不仅这些。

第7章讲了通过各种IPC手段,如管道,socket等,分解任务,以不同的程序去实现目标。

第8章讲了使用微型语言(如正则表达式,m4模板),第9章讲了代码生成以及数据驱动编程。

第16章讲到另一句名言:不要重新发明轮子。

最后一章,作者也少见的讲了Unix系统的各种缺陷。

作为读书笔记,到这里就可以了。