Posted on 2010-01-10 20:25
dennis 閱讀(2956)
評論(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、函數式編程,解決的問題都是為了更好的抽象,抽象是個好東西,但是抽象無法解決那些本質性的問題,因此《人月神話》斷言沒有銀彈,我們仍然需要跟狼人作戰。