注:本文仅供学习使用,请勿用作其他用途,谢谢!
自 底 向 上 设 计
邓 辉 (翻译)
长期以来,我们一直遵循这样一个编程风格方面的原则:一个程序的功能要素不应该太大。如果程序中的某些部件的规模超出了易于理解的范围,就会造成大量的复杂性,这些复杂性很容易隐藏错误,正如在一个大城市中很容易隐藏罪恶一样。这种程序难以理解、难以测试、难以调试。
根据这个原则,一个大型程序必须要分割成一些小块,并且分割的程度应该和程序的规模相称。那么,如何来对程序进行分割呢?传统上,我们一般会使用称为自顶向下的设计方法(top-down design):程序打算完成7项任务,于是就把它划分成7个主要的子例程。第一个例程要做4件事情,于是就再把它分割成4个子例程。这个过程要持续下去,直到整个程序具有适当的粒度为止。这里适当的含义为:每个部分都大到足以完成一些实质性的工作,并且都小到足以作为一个可理解的单元。
有经验的Lisp程序员却以一种不同的方式来分割他们的程序。和自顶向下设计对应,他们称之为自底向上设计(bottom-up design):改变语言,使之适合于要解决的问题。在Lisp中,你不是仅仅用语言来编写程序,同时你还要对语言进行增进,使之更加适用于你的程序。在你编写程序时,你会这么想:“我希望Lisp具有某某操作符。”于是你就去把它编写出来。然后,你发现使用这样一个新的操作符会简化程序其他部分的设计。语言和程序在一起演化。就像是位于两个处在战争状态国家的边界一样,语言和程序之间的边界线不断地重新划分,直到最后停留在山川、河流区域――最适合于你的问题的一个自然边界。最后,你的程序看上去就像是使用专门为它设计的语言编写的。当语言和程序相互非常匹配时,代码也会变得更加的清晰、短小、高效。
值得强调的是,自底向上设计并不仅仅是一种以不同的顺序编写同一个程序。当以自底向上方式工作时,通常得到的是一个完全不同的程序。得到的不是一个单片机式的程序,而是一个具有更多抽象操作符的、语义更为丰富的语言,以及一个用该语言编写的更短小的程序。不光得到了结果,你还得到了更强大的表达能力。
我们在编写代码时,一旦剥离出了那些仅仅做一些薄记工作的部分,剩余的核心部分就会变得非常短小,我们把语言的抽象层次提得越高,从顶部到达它得距离就越短。这会带来如下几个好处:
1、 通过使语言做更多的工作,自底向上设计所产生出的程序更加短小、更加敏捷。一个短小的程序就不必再分割成很多的部分,而具有较少的部件则意味着程序将更加地易读和修改。具有较少部分的程序也意味着部件之间具有更少的关联,因此出错的可能性就更少。正如一个工业设计师努力减少机器内部中的运动部件的数量一样,有经验的Lisp程序员也在使用自底向上设计来减少他们程序的规模和复杂性。
2、 自底向上设计鼓励代码重用。当你编写了两个或者更多的程序时,很多你为第一个程序编写的工具也会在后续的程序中发挥作用。一旦你具有了一个大型的工具基础库,编写一个新程序所需要的工作量将会大大低于使用原始Lisp所需要的。
3、 自底向上设计使得程序更加易读。是一个通用目的的操作符好理解呢,还是一个针对特定领域的特定目的操作符好理解呢?
4、 自底向上设计有助于理清自己的设计思路,因为它会迫使你总是去寻找代码中的模式。如果两个不同的部件在形式上非常相似,你就会被引导去注意这些相似性,并且以一种更为简单的方法去重新设计程序。
除了Lisp外,在其他程序语言中也可以在某种程度上实施自底向上设计。只要你看到了库函数,在那里就使用了自底向上设计。然而,Lisp在这方面提供了更为宽广的支持, 使之在以Lisp风格编程方面达到了一种:Lisp不仅仅是一种不同的语言,而是一种完全不同的编程方法的程度。
很显然,这种开发风格更适合于小团队开发。但是,它同时也延伸了一个小团队所能完成的任务的限制。在《人月神话》一书中,Frederick Brooks指出,程序员团队的生产力不会随着团队的规模线性增长。当团队的规模扩大时,个体程序员的生产力会下降。Lisp编程的经验则以一种更为令人振奋的方式表达了这一法则:随着团队规模的缩小,个体程序员的生产力会升高。一个小规模的团队会取得成功,这只是因为它更小一些。当一个小型团队同时也掌控了Lisp所使能的技术时,那么它就会更为彻底地取得胜利。