UNIX 哲学

(摘自《UNIX  编程艺术》)

一、一言以譬之

KISS——Keep It Simple, Stupid!

二、原则汇总

  1. 模块原则:使用简洁的接口拼合简单的部件。
  2. 清晰原则:清晰胜于机巧。
  3. 组合原则:设计时考虑拼接组合。
  4. 分离原则:策略同机制分离,接口同引擎分离。
  5. 简洁原则:设计要简洁,复杂度能低则低。
  6. 吝啬原则:除非确无他法,不要编写庞大的程序。
  7. 透明性原则:设计要可见,以便审查和调试。
  8. 健壮原则:健壮源于透明与简洁。
  9. 表示原则:把知识叠入数据以求逻辑质朴而健壮。
  10. 通俗原则:接口设计避免标新立异。
  11. 缄默原则:如果一个程序没什么好说的,就沉默。
  12. 补救原则:出现异常时,马上退出并给出足够错误信息。
  13. 经济原则:宁花机器一分,不花程序员一秒。
  14. 生成原则:避免手工 hack,尽量编写程序去生成程序。
  15. 优化原则:雕琢前先要有原型,跑之前先学会走。
  16. 多样原则:绝不相信所谓“不二法门”的断言。
  17. 扩展原则:设计着眼未来,未来总比预想来得快。

三、原则阐释

1 模块原则

  • “计算机编程的本质就是控制复杂度”。排错占用了大部分的开发时间,弄出一个拿得出手的可用系统,通常与其说出自才华横溢的设计成果,还不如说是跌跌撞撞的结果。
  • 没有万能药。(没有银弹)
  • 要编制复杂软件而又不至于一败涂地的唯一方法就是降低其整体复杂度——用清晰的接口把若干简单的模块组合成一个复杂软件。如此一来,多数问题只会局限于某个局部,那么就还有希望对局部进行改进而不至于牵动全身。

2 清晰原则

  • 程序是写给人看的。
  • 在选择算法和实现时就应该考虑到将来的可扩展性。不要为了一丁点的性能提升就大幅增加技术的复杂性和晦涩性。
  • 优雅而清晰的代码不仅不容易崩溃——而且更易于让后来的修改者立刻理解。

3 组合原则

  • 如果程序彼此之间不能有效通信,那么软件就难免会陷入复杂度的泥淖。
  • 在输入输出方面,Unix 传统极力提倡采用简单、文本化、面向流、设备无关的格式。在经典的 Unix 下,多数程序都尽可能采用简单过滤器的形式,即将一个输入的简单文本流处理为一个简单的文本流输出。
  • 要想让程序具有组合性,就要使程序彼此独立。
  • GUI 可以是个好东西。但是在做一个 GUI 前,应该想想可不可以把复杂的交互程序跟干粗活的算法程序分离开,每个部分单独成为一块,然后用一个简单的命令流或者是应用协议将其组合在一起。
  • 把尽可能多的编程元素组织为一套定义良好的 API。

4 分离原则

  • 策略的变化要远远快于机制。
  • 将接口和引擎剥离开来。两个方法:
    1. 将应用按照一个库来写,这个库包含许多由内嵌脚本语言驱动的 C 服务程序,而至于整个应用的控制流程则用脚本来撰写而不是用 C 语言。(如 Emacs 编辑器,采用 Lisp + C)
    2. 前后端分离。前端实现策略,后端实现机制。

5 简洁原则

  • 他们的设计能力大大超出他们的实现和排错能力,结果便是代价高昂的废品。
  • 许多优秀的设计被市场推销所需要的大堆大堆“特性清单”扼杀——实际上,这些特性功能几乎从未用过。然后,恶性循环开始了:比别人花哨的方法就是把自己变得更花哨。很快,庞大臃肿变成了业界标准,每个人都在使用臃肿不堪、bug 极多的软件。
  • 需要鼓励另一种软件文化,以简洁为美,人人对庞大复杂的东西群起而攻之——这是一个非常看重简单解决方案的工程传统,总是设法将程序系统分解为几个能够协作的小部分,并本能地抵制任何用过多噱头来粉饰程序的企图。

6 吝啬原则

  • 程序大了,维护起来就困难。

7 透明性原则

  • 一开始就多做点工作以减少日后调试的工作量会很划算——设计时充分考虑透明性和显见性。
    • 透明性:一眼就能看出软件是在做什么以及怎样做的;
    • 显见性:带有监视和显示内部状态的功能。
  • 调试选项的设置应该尽量不要在事后,而应该在设计之初便考虑进去。
  • 程序应该能够把原开发者解决问题的思维模型告诉后来者。
  • 提倡接口简洁,以方便其他程序对其进行操作——尤其是测试监控工具和调试脚本。

8 健壮原则

  • 软件不仅在正常情况下运行良好,而且在超出设计者设想的意外条件下也能够运行良好。
  • 大多数软件禁不起磕碰,毛病很多,就是因为过于复杂,很难通盘考虑。如果不能正确理解一个程序的逻辑,就不能确信其是否正确,也就不能在出错的时候修复它。让程序的内部逻辑更易于理解:透明化和简洁化。
  • 设计时要考虑到能承受极端大量的输入。
  • 避免在代码中出现特例。

9 表示原则

  • 数据要比编程逻辑更容易驾驭。如果要在复杂数据和复杂代码中选择一个,宁愿选择前者。更进一步:在设计中,应该主动将代码的复杂度转移到数据之中。

10 通俗原则

即“最小惊奇原则”。

  • 最易用的程序就是用户需要学习新东西最少的程序——最切合用户已有知识的程序。
  • 关注目标受众。关注传统惯例。

11 缄默原则

  • 若程序没有什么特别之处可讲,就保持沉默。行为良好的程序应该默默工作,决不唠唠叨叨,碍手碍脚。沉默是金。
  • 如果显示的信息都是重要的,那就不用找了。
  • 设计良好的程序将用户的注意力视为有限的宝贵资源,只有在必要时才要求使用。

12 补救原则

  • “宽容地收,谨慎地发”。
  • 软件要尽可能从容地应付各种错误输入和自身的运行错误。但是,如果做不到这一点,就让程序尽可能以一种容易诊断错误的方式终止。

13 经济原则

  • 大多数的应用场合都应该使用高一级的语言。

14 生成原则

  • 人类很不善于干辛苦的细节工作。因此,程序中的任何手工 hacking 都是滋生错误和延误的温床。程序规格越简单越抽象,设计者就越容易做对。由程序生成代码几乎(在各个层次)总是比手写代码廉价而且更值得信赖。

15 优化原则

  • 做好原型设计可以帮助你避免为蝇头小利而投入过多时间。
  • “过早优化是万恶之源”。
  • 先求运行,再求正确,最后求快。
  • 借助原型化找出哪些功能不必实现,有助于对性能进行优化;那些不用写的代码显然无需优化。目前,最强大的优化工具恐怕就是 delete 键了。

16 多样原则

  • Unix 奉行的是广泛采用多种语言、开放的可扩展系统和用户定制机制。

17 扩展原则

  • 决不要认为自己找到了最终答案。
  • 要为数据格式和代码留下扩展的空间,否则,就会发现自己常常被原先的不明智选择捆住了手脚,因为你无法既改变它们又维持对原来的兼容性。
  • 设计协议或是文件格式时,应使其具有充分的自描述性以便可以扩展。(如版本号)
  • 设计代码时,要有很好的组织。
  • 程序接合部要灵活,在代码中加入“如果你需要……”的注释。
  • 设计为将来着眼,节省的有可能就是自己的精力。

四、态度

  • 看到该做的就去做——短期来看似乎是多做了,但从长期来看,这才是最佳捷径。
  • 不断追求卓越。你必须相信,软件设计是一门技艺,值得你付出所有的智慧、创造力和激情。否则,你的视线就不会超越那些简单、老套的设计和实现;你就会在应该思考的时候急急忙忙跑去编程;你就会在该无情删繁就简的时候反而把问题复杂化——然后你还会反过来奇怪你的代码怎么会那么臃肿、那么难以调试。
  • 珍惜你的时间绝不浪费。一旦某人已经解决了某个问题,就直接拿来利用,不要让骄傲或偏见拽住你又去重做一遍。永远不要蛮干;要多用巧劲,省下力气到需要的时候再用,好钢用在刀刃上。善用工具,尽可能将一切都自动化。
  • 软件设计和实现应该是一门充满快乐的艺术,一种高水平的游戏。需要用心,需要去游戏,需要乐于探索。