我和几乎所有的Common Lisp和CLOS(Common Lisp Object System)的设计者都极度深受麻省理工学院/斯坦福大学(MIT/Stanford)设计风格的影响。这种风格的本质可以用“正确的做法(the right thing)”这个短语来概括。对于这样的设计者,重要的就是要遵循下面的这些设计理念:
简单性—设计必须简单,这既是对实现的要求,也是对接口的要求。接口的简单要比实现的简单更加重要。
正确性—设计在任何值得注意的方面都要保证正确。不正确是绝对不允许的。
一致性 — 设计必须保持一致兼容。设计可以允许轻微少量的不简单和不完整,来避免不一致。一致性和正确性同等重要。
完整性—设计必须覆盖到实际应用的各种重要场景。所有可预料到的情况都必须覆盖到。简单性不能过度的损害完整性。
我相信大部分的人都会同意,这些理念都是一些好的实施原则。我把采用这种设计哲学的方法叫做“麻省理工方法(MIT approach)”。Common Lisp(包括CLOS)和Scheme代表着麻省理工方法的设计和实现。
“差点的更好(Worse is Better)”的理念只是稍微有点不同:
简单性—设计必须简单,这既是对实现的要求,也是对接口的要求。实现的简单要比接口的简单更加重要。简单是设计中需要第一重视的因素。
正确性—设计在任何值得注意的方面都要求正确。为了简单性,正确性可以做轻微的让步。
一致性— 设计不能过度不兼容一致。为了简单,一致性可以在某些方面做些牺牲,但与其允许设计中的这些处理不常见情况的部分去增加实现的复杂性和不一致性,不如丢掉它们。
完整性 — 设计必须覆盖到实际应用的各种重要场景。所有可预料到的情况都应该覆盖到。为了保证其它几种特征的品质,完整性可以作出牺牲。事实上,一旦简单性受到危害,完整性必须做出牺牲。一致性可以为实现的完整性作出牺牲;最不重要的是接口上的一致性。
早期的Unix和C语言是使用这种设计理论的代表,我把采用这种设计策略的方法叫“新泽西方法(New Jersey approach)”。我一直在有意的讽刺这种“差点的更好”理论,让人们相信它显然是一种不好的理论思想,新泽西方法就是一种不好的软件实现方法。
然而,我相信,即使在一种假象的情况下,这“差点的更好”理论也要比“正确的做法”理论有更大的生存空间,也就是说,在软件开发上,新泽西理论要比麻省理工理论更实用。
让我来复述一个故事,向大家展示麻省理工方法和新泽西方法之间的真实区别,以及为什么各个理论的支持者都完全的相信他们的理论更好。
两位著名的人物,一位是来自麻省理工,另一位来自伯克利学院(但是研究Unix的)。一次,他们遇到一起讨论操作系统问题。来自麻绳理工的人对ITS(麻省理工学院人工智能实验室的操作系统)非常熟悉,并阅读过Unix的源代码。他对Unix如何解决PC机的loser-ing问题非常有兴趣。当一个用户程序调用系统例程去执行一个长时间的、并包含有重要状态的操作时,例如IO缓冲,loser-ing问题就有可能出现。如果在执行这个操作时,发生了中断,用户程序的状态必须被保存下来。因为对系统例程的调用通常是单指令的,运行用户程序的PC机无法捕捉到例程的过程状态。系统例程要么退出,要么强行继续运行。“正确的做法”是退出,复原用户程序调用系统例程的指令,让用户程序在中断之后能重新恢复运行,例如,重新进入系统例程。这被叫做“PC loser-ing”,因为PC机被强制进入一种“弱势(loser)模式”,其中,“弱势”者是麻省理工对“用户”的一种爱称。
麻省理工的人没有看到有任何的用来处理这种情况的代码,问新泽西人,Unix是如何处理这种问题的。新泽西人说,Unix人清楚这个问题,但提供的解决方式是针对系统例程通常能正常完成的情况的,当系统例程不能成功的完成运行时,它会返回一个错误码,指示操作运行失败。一个正确的用户程序这时需要去检查这个错误码来决定是否需要再次调用这个系统例程。麻省理工人不喜欢这个解决方案,因为这不是“正确的做法”。
新泽西人说,Unix的解决方案是正确的,因为Unix的设计理论是追求简单,而这“正确的做法”太复杂。除此之外的好处是,程序员能容易的添加这种错误探测,重复他们的操作。麻省理工人指出,这种实现方案确实简单,功能性上的接口却变的复杂。新泽西人指出,这就是Unix在设计上做出的合适的取舍。实现上的简单比接口上的简单更重要。
麻省理工人这时嘟囔着说:有时你需要让一个强壮的人去变成一种软弱的小鸡。新泽西人没明白他是什么意思(我也不太明白)。
现在,我开始主张“差点的更好”确实是更好。C语言是一种为开发Unix而设计的语言,它的设计采用的是新泽西方法。C语言因此是一种很容易就能写出漂亮的编译器的语言,它要求程序员编出的代码要易于编译器去解释。有些人称C语言为高级汇编语言。早期的Unix和C编译器都非常的简单,易于移植,需要很少的硬件资源来运行,它提供了你从一个操作系统和编程语言里想得到50%—80%的功能。
现有的机器有一半在任何方面都低于中等配置水平(更小,更慢)。而Unix和C语言在它们上面运行良好。“差点的更好”理论表明实现的简单性具有最高的优先级,这意味着Unix和C语言很容易在这些机器上进行移植。因此,如果任何一台机器,Unix和C能在功能性上提供50%的支持,那它就会无处不在了。Unix和C就是这样,不是吗?
Unix和C语言是终极电脑病毒。
“差点的更好”理论另外一个好处是,程序员可以有条件的牺牲某些安全性,方便性,全力去获得优良的性能和较少的资源使用。使用新泽西方法开发的软件既能在大机器上运行,也能在小机器上运行,程序具有很好的可移植性,这是因为它们是在一个病毒程序是写出来的。
有一点很重要,初始病毒必须基本上好用。病毒的传播由于它的可迁移性而得到保证。一旦病毒传播开来,迎来的压力会促使它进一步改进,促使增加功能至接近90%完备的水平,但用户此时已经有条件的习惯了这种比“正确的做法”差一点的东西了。所以,“差点的更好”的软件会首先获得人们的接受,然后会有限制的让用户降低期望,最后进行改进,直至接近“正确的做法”。在实际情况中,1987年的Lisp编译器当时和C编译器都是非常的优秀,但是很多的编译器专家仍然努力让C编译器做的更好。
1995年的好消息是我们有了一个好的操作系统和编程语言;而坏消息是它们分别是Unix和C++。
“差点的更好”还有最后一个好处。因为新泽西式的语言和系统不够真正的强大来开发出复杂巨型的软件,大型系统必须在设计上进行组件重用。因此,一种整合的传统就此迅速出现了。
那“正确的做法”的表现如何呢?我们有两种常见的模式:“复杂的大型系统”模式和“钻石类珍宝“模式。
“复杂的大型系统“模式通常像这样:
首先,”正确的做法“需要去设计。然后实现过程需要去设计。最后,进行实现。因为这是”正确的做法“,它会提供100%预期的功能,实现的简单性从来不是一个可考虑的因素,所以你要用很长的时间去完成它。它巨大而且复杂。它需要复杂的工具,工具需要能正确的使用。其中20%的功能会花去你80%的精力,所以,”正确的做法“需要很长的时间来完成,它的运行只有在采用先进技术的硬件上才会表现的令人满意。
“钻石类珍宝“模式通常表现如下:
”正确的做法“花了大量的时间去设计,但这种方式,在单个功能点上,其实并没有占多大比重。这种设计的实现,如果想让它运行的快,要么是根本不可能,要么是超出了大多数开发者的能力。
头一种模式也是经典的人工智能软件的开发模式。
”正确的做法“出来的通常是大型的软件,但除了”正确的做法“会把软件设计的巨大外,没有其它的理由造成这种局面。也就是说,大型软件里很多功能是偶然会用到的。
从这些事情中我们学到的知识是,人们通常不喜欢按照”正确的做法“做事。但你最好要采纳一半的”正确的做法“,让你的软件能像病毒一样流传。一旦人们被它吸引,花时间去改进它,使它接近90%的”正确的做法“。