一个程序的函数元素不能太大是一个长期存在的编程风格原则。如果程序的某些组件增长到读难以理解的成都,它就变成一大堆的复杂东西,它们隐藏错误就像一个大城市隐藏逃亡者一样简单。这样的软件将很难读、很难测试、很难杀臭虫。
遵循这个原则,一个大的程序必须被分成很多片,一个程序越大,就需要被越多地分割。你怎样分割一个程序呢?传统的方法是自上而下的设计:你说“这个程序的目的是做这7件事情,所以我把它分成7个子功能”等等。这个过程持续到整个程序有正确水平的粒度——每块都可以独立地做一些事情,但是能够作为一个单元小到可以读懂。
有经验的Lisp程序员以不同的方式来分割他们的程序。就像自上而下的设计,他们遵循自下而上的设计——改变语言以适应问题。在Lisp中,你不仅仅是根据语言去写你的程序,你也根据你的程序区建立语言。当你正在写一个程序的时候,你可能想"我希望Lisp有这样这样的操作符",所以你就去写它。之后,你意识到使用新的操作符将简化程序其它部分的设计,等等。语言和程序一起演进。就像两个正在战争的国家的边界一样,语言和程序的边界被不断地重画,知道最后沿着山脉和河流,你问题的自然边缘。最后你看的程序,就像这门语言就是为它设计的一样。党语言和程序相互协调很好,你得到的将是清晰、小巧和高效的代码。
值得强调一下,自下而上的设计不仅仅意味着以不同的顺序写同一个程序。当你自下而上的工作,你通常最后会得到一个不同的程序。你将得到一个拥有更多抽象操作符的更大的语言和用它写的更小的程序,而不是单一的、庞大的程序。你将得到一个拱门,而不是一个横梁。
典型的代码中,一旦你抽象出仅仅记账的部分,剩下的就很简短了;你在越高的层级上创造语言,贯穿它所需的距离就越少。这带来了几个好处:
1. 通过让语言做更多的工作,自下而上的设计产生的程序更加小巧和敏捷。一个小的程序不必要被分成那么多的组件。越少的组件也意味着组件之间的关系也越少,也就意味着那里有更少的机会出错。工业设计人员尽可能减少机器中需要移动的部分,有经验的Lisp程序员使用自下而上的设计来减少程序的体积和复杂度。
2. 自下而上的设计提升了代码的可重用性。当你写两个或者更多程序时,为第一个程序写的一些工具对随后的程序也很有用。一旦你拥有了大量的工具,在你开发一个新程序的时候花费的精力只有用原生的Lisp的一小部分。
3. 自下而上的设计使得程序更加易读。这种类型的抽象让读者理解一个通用的操作符;函数抽象让读者去理解一个特定的子功能。
4. 因为它使得你一直关注于你代码的模式,自下而上的工作会帮助你澄清你程序设计的想法。如果两个相隔较远的模块在形式上是相似的,你将被引导注意到这种相似性,可能你就会重新以一种更简单的方式来设计这个程序。
自下而上的设计在非Lisp语言中也可以在一定程度上实现。当你看到库函数的时候,就是在进行自下而上的设计。然而,Lisp在这方面给与你更宽广的力量,并且使语言在Lisp风格的上扮演越来越重要的角色——Lisp不仅仅是一种不同的语言,也是一种完全不同的编程方式。
这种开发风格适合于那种可以分成几个小组来开发的程序。然而,同时,它扩展了一个小模块的限制。人月神话中,Frederick Brooks指出一个小组的的生产力和程序员的数量不成正比。随着小组的扩大,每个程序员的生产力开始下降。Lisp编程的经验以一种愉快的方式来表达这个规则:随着组的缩小,每个程序员的生产力上身。一个小组成功,相对来说,仅仅是因为它小。当一个小组可以利用Lisp所带来的技术时,它可以一下子成功。