Common Lisp专家Peter Seibel对Google公司首席Java架构师Joshua Bloch的访谈(摘一段)

API对设计流程的影响

  Seibel:你设计软件的流程是什么样的?打开Emacs就开始写代码,然后改来改去直到程序写好?还是坐到沙发上拿着一打纸先列个提纲?

  Bloch:很多年前,我在OOPSLA(译者注:面向对象编程、系统、语言和应用国际研讨会。)上作了一个演讲,题目是“如何设计一个好的API,以及这为什么很重要”。网上可以找到这个演讲的几个版本。它很好地解释了我的设计流程。

  最重要的是了解你到底要设计什么,也就是你要解决的是什么问题。需求分析的重要性怎么强调也不过分。有人认为:“噢,需求分析呀。跑到顾客那边问问他需要什么。得到客户的答案不就成了嘛。”

  事实绝非如此。这不仅是一个协商的过程,而且是一个理解的过程。许多顾客不会告诉你问题,而会告诉你一个解决方案。例如,顾客可能会说:“我需要你给这个系统加上以下17个特性。”那么你必须问:“为什么?你想用这个系统做什么?你期望它怎么发展?”等等。你要来来回回好几次,直到弄明白顾客真正需要软件去做的所有事情。这些就是用例。

  这个阶段最重要的事情就是提出好的用例。一旦有了用例,你就有了用来比较所有备选解决方案优劣的基准。你可以花大量的时间去改进用例,因为一旦用例错了,你就彻底失败了,所有后续的流程都会徒劳无功。

  我见过这样的事。有人找来一帮聪明人,还没搞清到底要做个什么样的系统,就开工了。辛苦地工作了6个月,写出来247页的系统规范文件。这是最糟糕的情况。因为6个月后他们精确制定出来的系统可能毫无用处。他们往往会说:“我们已经投资了那么多,制定出来规范文件,我们必须把这个系统做出来。”所以他们创造了一个没有任何用处的系统,这个系统也从未投入使用过。多恐怖啊。如果没有用例就做好了软件,那么当你试图做点非常简单的操作时就会发现:“哦,我的天,像选择一个XML文档并打印这么简单的事情,需要这么多的代码啊。”这是很恐怖的。

  所以先获取这些用例,然后编写骨架API。骨架API应该很短很短,也就一页纸的内容吧,一般正好是一页,无需非常精确。你要声明包、类和方法,如果还不清楚他们应该是什么样的话,可以放一句话的描述。不过这不是产品发布要求的那种质量文档。

  中心思想就是在这个阶段保持敏捷,逐步完善API,使其满足用例,为原始的API添加代码,看是否可以满足需求。真是不可思议,很多事情事后看真是太浅显了,但设计API的时候,甚至是构思用例时,你还是会犯各种错误。用代码实现用例时你会说:“哦,我的天,全都错了。类太多了。这些可以合并,这些需要拆开。”或者类似这样的话。好在API文档只有一页长,改起来也很容易。

  你对API越来越有信心,代码也就越写越长。但是,核心原则是,先写使用API的代码,然后再写实现它们的代码。因为,如果实现代码被废弃,之前的工作就都白做了。事实上,应该在给出设计规范前写API的代码,否则你可能把时间浪费在给最后完全不需要的东西设计规范上。这就是我设计软件的方法。

  Seibel:设计Java集合类这样的,一个具体的自包含的API,设计规范需要有多具体?

  Bloch:我敢说比你想的要粗略多了。任何复杂的编程都需要API设计,因为大程序都需要模块化,你必须设计模块之间的接口。

  优秀的程序员把问题分块,孤立地去看他们。这样做的理由有几条。比如,你可能会在无意中创造出好用的、可重用的模块。如果你写一个单一的系统,它越来越大,等你想分块的时候,就无法找到清晰的边界,最后系统就变成了一个无法维护的垃圾。所以我断言,无论你是否把自己看成API设计者,把问题分块都是最好的编程方法。

  这就是说,编程的世界非常广阔。如果对你来说编程就是写HTML代码,那么这也许不是最好的编程方法。但是,我认为对于大多数编程来说,这就是最好的方法。

  Seibel:所以你希望系统由不同的模块松散地耦合在一起。要达到这样的目标,现在有两种不同的看法。一种是坐下来实现设计模块间的API,像你前面提到的那样。另外一种是,“构建可运行的最简系统,然后毫不留情地重构”。

  Bloch:我不认为这两种方法有什么冲突。某种程度上,我谈的就是测试先行编程,以及对API的重构。如何测试你的API呢?在实现API之前编写它的测试用例。虽然我还不能运行用例,但我在进行测试先行的编程:实现用例后看API是否能完成任务,我用这样的方法测试API的质量。

Seibel:也就是说你写好使用API的用户代码,然后评审代码:“这就是我要的代码吗?”

  Bloch:对!有时候你都不用走到评审用户代码的这个阶段。写代码的时候可能就会有感悟,“写不出来,我忘了这部分API的功能了。”或者“这代码写起来太乏味了,一定哪里出错了。”

  这跟你多么优秀无关。不用API写代码,就不可能看出API有什么问题。设计了一个东西,使用了才知道:“哦,错的这么离谱。”如果这是在你浪费大量时间基于这个API写了无数代码之前的话,那么这就是一个重大的胜利。所以,我谈得更多的是测试先行编程和对API的重构,而不是重构API的实现代码。

  说到能够运行的最简程序,我完全赞同这种提法。API设计有一条基本原则:疑则不用。它必须是完全满足你关心的所有用例的最简系统。而不是说“把乱七八糟的代码堆在一起”。有很多格言警句说明了这点。我最喜欢的一条是:“简单没那么容易做到。”坊间认为就是Thelonious Monk说的,实际不是,是误传。

  没人喜欢烂软件。人们提倡“构建可运行的最简系统,然后毫不留情地重构”,而不提倡“写垃圾代码”,更不会说“不要做前期设计”。我曾跟Martin Fowler讨论过这个问题。他坚信,只有仔细推敲要做的东西,系统才会有合理的形状和结构。他说过,“不要在写代码前先写下247页的设计规范。”我很赞同。

  我不赞同Martin的一点是:我认为测试不能用来取代文档。只要你写了别人编程时可以利用的代码,你就需要做出精确的说明,而测试确保这些代码符合你给出的说明。

  所以两大阵营确实有些不同意见,但是我认为他们之间的鸿沟没有某些人想象的那么大。

  Seibel:既然你提到了Fowler,咱们就聊聊他。他写了很多关于UML的书,你把UML当设计工具用过吗?

  Bloch:没有。我觉得用UML做些图表让其他人理解起来可能更容易。但是说实话,我根本记不住那些组件应该是方的还是圆的。

posted on 2010-12-28 13:35  longhuihu  阅读(158)  评论(0编辑  收藏  举报