第 1 章 哲学 末,将 Unix 的哲学概括为“KeepItSimple,Stupid!”。贯穿整个“设计”部分的不变主题就是尽可能保持简单设计的重要性。但什么是“尽可能的简单”?又如何断定?

本章并不是简单地尝试给出这些问题的答案,因为根本没有答案一一而是希望读者能够在概念上有所收益,从而挖掘出自己的答案。

#13.1 谈谈复杂度

#13.1.1 复杂度的三个来源

Unix 程序员追求简单的激情,源自注重实效的事实: 复杂度就是成本。复杂的软件更难于开发,难于测试,难于调试,难于维护一一最重要的,难以学习和使用。而复杂所带来的成本,开发时便汹涌而来,部署后更变本加厉。复杂是 bug 滋生的温床,在整个的软件生存期,世界都将不得安宁。

复杂度的三个来源:

  • 实现复杂度:程序员为了能够试图理解一个程序,从而建立其思维模型并调试该程序的困难程度
  • 接口复杂度:即界面复杂度。对用户而言,复杂度与记忆负担紧密关联。
  • 代码量:代码的缺陷密度,每百行代码出错率,往往是一个与实现语言种类无关的常量。更多行的代码意味着更多的 bug,而调试常常是开发中最昂贵、最耗时的部分。

所有种种压力将程序员拖进复杂度的泥沼中,其中两个最臭名昭著的就是功能蠕变和过早优化。

  • 代码量、接口复杂度和实现复杂度可能同时上升。这就是功能蠕变的通常结果,所以程序员对其特别恐惧。
  • 过早的优化并不增加接口复杂度,但是对于实现复杂度和代码库规模有着负面的影响(常常特别糟糕)。

三种复杂度陷阱:

  • 设计时首先考虑容易实现或代码规模,可能就会简单地将许多底层任务都抛给用户。——> Manularity 陷阱
  • 迫于保持代码库适度规模的压力不得不使用极端晦涩复杂的实现技法,这往往会导致系统实现复杂度的层层叠加,成为无法调试的一团乱麻。——> Blivet 陷阱
  • 如果项目设计者对实现复杂度特别敏感,而拒绝使用统一但复杂的方式来解决一整类问题,相反地更愿意对问题个体编写重复、专用代码,其结果就是代码库尺寸暴涨,而维护问题远较使用统一方法严重。——> Adhocity 陷阱

#13.1.3 本质的、选择的和偶然地复杂度

在理想世界,Unix 程序员只愿意手工打造小巧完美的软件宝石,每个都那么小巧、那么优雅、那么完美。然而现实中很不幸的是,太多复杂问题需要复杂的解决方案。仅十行的程序,再优雅也无法控制喷气客机。

  • 本质复杂度:喷气客机的复杂是必然的。过去有个相当尖锐的观点,不能为简单性而牺牲掉功能,因为飞机必须要能飞。
  • 偶然复杂度:没有找到实现规定功能集合的最简方法,偶然复杂度可以由良好的设计或重新设计来去除
  • 选择复杂度:同某个期望的功能相关联,只能由改变工程的目标来去除

#13.1.4 映射复杂度

复杂度的不同来源必须以不同方法应对:

  • 代码库规模可以采用更好的工具来解决
  • 实现复杂度可以选择更好的算法来处理
  • 接口复杂度必须着眼于更好的交互设计,一种考虑了人类工程学和用户心理学在内的技能。这种技能,比编码能力更为少见。

处理各种复杂度,必然更仰赖于见识而非方法

  • 通过发现更简单的方法可以去除偶然复杂度。
  • 依赖上下文环境判断哪些功能值得去做,可以去除选择复杂度。
  • 去除本质复杂度,就只能通过对现实真谛的洞察和顿悟,从根本上重新定义所要解决的问题。

#13.1.5 当简洁不能胜任

在 Unix 传统中存在一个强烈的偏好,宁可去掉功能,也不可接受可能复杂性

同其他美学形式一样,我们需要注意何时设计上的简约已经的不再是有价值的自律形式,而开始成为一件伪装的苦行者外衣———一种实际上把美德作为借口来敷衍工作的纵容方式。

#13.4 软件的适度规模

软件很容易负载太多任务、太多需求设想而最终失败,也很容易把程序编制得过于复杂、臃肿和庞大。… 纠正这种趋势的方法就是吝啬原则:只有实证了其他方法行不通时才写庞大程序,即 吝啬原则:除非确无它法,不要编写庞大的程序