Posted on 2010-01-10 20:25
dennis 阅读(2946)
评论(4) 编辑 收藏 所属分类:
涂鸦 、
计算机科学与基础
《Joel on software》谈到所谓抽象漏洞,简单来说就是抽象能解决90%的一般情况,而其他10%的情况你仍然需要跟抽象层面下的细节打交道,也就是抽象本身只能减少你的工作时间,而无法减少你的学习时间。道理简单,举几个例子。
以SQL语言为例,SQL是所谓说明性的语言,你所写的语句只是一条what,而how是如何做的无需关心,但是真的无需关心吗?事实上是不行,低效的SQL语句对数据库的性能损害非常大,作为程序员你需要知道SQL这个抽象层次下的部分内容,知道数据库是怎么执行这些语句,知道如何去避免一些最差实践。再比如分布式调用希望做到能跟本地调用一样的透明,但实际上还是不行的,网络的不确定性让RPC调用根本无法做到的类似本地调用那样的透明性。隐藏在RPC这个抽象层次下的网络通信细节,你不能不去care。抽象能帮你解决大多数情况,提高你的工作效率,但是剩下的一公里问题,仍然需要你花费更多时间和精力去了解并解决。这事实上也是一个优秀程序员跟普通程序的差别之一,学习了java编程,知道了collection集合框架,不代表你无需再去学习数据结构和算法。
Joel将这个现象称为漏洞抽象。事实上,我并不认为这是抽象本身的漏洞,这反而是软件的本质复杂性在作怪,抽象只能去化简偶然复杂性,例如函数、类、模块化等手段去组织代码,而本质的复杂性是无法避免的。举个不是那么恰当的例子,例如我们有这么个方法,传进一个参数list,我们要遍历list做一些事情,(我知道用迭代器才是正途,先允许我犯这么个错误),你可能这么写:
public void doSomething(List<String> list){
for(int i=0;i<list.size();i++){
String str=list.get(i);
//do something
}
}
这样的代码我估计在1.5有for语句增强之前不少人都写过,这样的代码有什么问题呢?考虑下list是ArrayList和LinkedList这两种情况,List是链表的抽象,但是链表的实现形式却是可以用数组或者引用链接,链表的实现形式不同,List.get(index)这个方法的效率会很成问题。我们都知道ArrayList适宜于随机访问,而LinkedList方便插入添加移除,在这个doSomething方法中,显然随机访问的诉求大于添加移除。在通常情况下,这样写都不会成为问题,但是如果这个doSomething方法被经常调用,并且list是一个LinkedList的情况下,这个方法就很可能成为性能瓶颈。我们寄希望于List这个接口可以让我们无需关心list的具体实现,然而现实是你仍然需要知道各种实现的区别和原理,这就是所谓漏洞抽象。这并非抽象的无力,你肯定不会反对“针对接口编程”这条原则,而是抽象本身解决不了本质复杂性,这里的本质复杂性就是链表的实现方法,随机访问与添加移除的平衡问题。在我们无法找到更好的链表实现方法来平衡随机访问与添加移除之前,这个本质复杂性就不是抽象能够解决的。
同样的现象出现在String、StringBuffer、StringBuilder的使用上,字符串的实现方法你仍然需要知道,这是绕不过去的本质复杂性。这里谈到的本质复杂性根本上也是现实世界的本质复杂性的反映,扯远些就更虚了。就现实的工作情况来看,不知道其他人有没有这样的经验,就是在自以为解决某个难题的时候,最后却发现难题以另一个面目出现,问题本身没有得到解决,只是以更好的方式被掩盖了。
无论是过程式、OO、函数式编程,解决的问题都是为了更好的抽象,抽象是个好东西,但是抽象无法解决那些本质性的问题,因此《人月神话》断言没有银弹,我们仍然需要跟狼人作战。