作者:周思博 (Joel Spolsky)
譯:Paul May 梅普華
Monday, November 11, 2002
屬於 Joel on Software, https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/
你每天不可或缺的 Internet 裡有個關鍵的小魔法,這個魔法就在 TCP 通訊協定這個 internet 基礎協定裡。
TCP 是一種可靠的資料傳輸方法。我說可靠是指如果用 TCP 在網路上傳一個訊息,訊息一定會到,絕不會亂掉或壞掉。
TCP 的用途很多,比如抓取網頁資料或傳電子郵件都是。由於 TCP 這麼可靠,連那些挪用錢的東非人電郵(譯註:指有陣子常見到的騙人信)都能完整無缺的到達,真是好笑。
相對的有另一種叫 IP 的不可靠資料傳輸方法。IP不保證資料會傳到,就算到了資料也可能會亂掉。如果你用IP傳送一堆訊息,很可能只有一半的訊息到達,而且其中還有一些到達的順序和原先傳送時的順序不同,另外可能有幾個訊息的內容會變掉,可能變成可愛的猩猩寶貝照片,更可能變成一堆看不懂的垃圾,看起來就像外國垃圾信的標題一樣。
這裡就是魔法所在:TCP 是架在 IP 上面的。換句話說,TCP不得不靠一個不可靠的工具想辦法可靠地傳送資料。
為了說明這的確是個魔法,想想下面這個本質上相同(雖然有點滑稽),來自真實世界的情節。
想像你有個方法把演員由百老匯送到好萊塢,基本上就是讓人坐上車後開車橫越國家送過去。有些車會出車禍讓可憐的演員掛掉。有時候演員在路上喝醉了就去剃光頭或刺納粹刺青,結果變得太醜而不能在好萊塢工作。另外由於走的路線不同,演員到達的順序常會跟出發的順序不一樣。現在想像有個叫好萊塢快遞的新服務,可以把演員送到好萊塢,並且保證演員一定會 (a) 到達,並保證 (b)順序不變 而且 (c)狀態完美地到達。神奇之處在於好萊塢快遞除了原本的車子以外,並沒有新的運送方法。好萊塢快遞的作法是在每個演員抵達時檢查演員的狀況,如果狀況不佳就打電話請公司把該演員的雙胞胎送來。如果演員到達的順序不對,好萊塢快遞會照正確順序重新排好。如果51區有架大幽浮在內華達的高速公路上墜毀阻斷了交通,預定走這條路線的演員就會改走亞歷桑那州,好萊塢快遞甚至不會把事情告訴加州的導演。導演只會覺得演員來得比平常慢,他們甚至不會聽到幽浮失事的消息。
TCP的魔法大致上就是這樣。這種作法常被電腦科學家稱為抽象:把複雜許多的東西隱藏起來的一種簡化動作。結果很多電腦程式的設計都是在建立抽象機制。字串程式庫是什麼?它是一種偽裝,假裝電腦能像處理數字一樣輕易的處理字串。檔案系統又是什麼?也是一種偽裝,假裝硬碟並不是一堆不停旋轉,可以儲存位元的磁性碟片,而是一個有著層層目錄的階層式系統,可以存放一個個由一或多個位元組字串構成的檔案。
把話題拉回 TCP。稍早為了讓事情單純一點,我撒了一個小謊,而且現在有些人可能會因為這個謊氣得頭上冒煙。我說過 TCP 保證你的訊息會到達,其實並不會。如果你養的蛇把連接電腦的網路線咬斷了,就沒有任何 IP 封包可以通過,這時候 TCP 當然也不可能讓你的訊息抵達。如果你惹毛了公司的系統管理員,他們為了報復就把你接到已經超過負荷的集線器,因此只有部份的 IP 封包能通過,這時候 TCP 是會動,不過一切都會變得很慢。
這就是我稱之為抽象機制有漏洞的狀況。TCP 試圖提供一個完整的抽象機制,想隱藏底下不可靠的網路,不過有時候網路會滲漏越過抽象機制,這時就會覺得抽象其實並不太能真的提供保護。這只是我所謂「抽象滲漏法則」的一個例子而已:
所有重大的抽象機制在某種程式上都是有漏洞的。
抽象會失效。有時候輕微有時候很嚴重,反正就是有漏洞。事情會因而出錯,而且當你有抽象機制時到處都可能會發生。下面有一些例子。
抽象滲漏法則會造成問題的原因之一,是因為它說明了抽象機制並不真能照原構想簡化我們的生活。當我想訓練某人成為C++程式師時,最好能完全不教char*和指標運算,直接去學STL字串。問題是總有一天他們會寫出"foo" + "bar"這樣的程式然後看到怪事出現,於是我就得停下來教他們有關char*的事情。他們也可能會試著呼叫某個需要OUT LPTSTR參數的Windows API函數,於是又得把char*、指標、Unicode、wchar_t以及TCHAR含入檔搞懂,才會知道如何呼叫。而這些全都是漏洞。
在教COM程式設計時,最好只要教學生如何使用Visual Studio的精靈和各個程式產生功能。不過萬一出了任何問題,他們根本不會知道怎麼回事,也不知道如何除錯或回復。我還是得教他們IUnknown和CLSID還有ProgIDS以及。哦,饒了我吧!
在教ASP.NET程式設計時,最好只要教學生可以在元件上雙擊,然後就能撰寫使用者點擊該元件時在伺服器執行的程式。不過處理超連結(<a>)點擊事件的HTML程式,和某個按鈕被按時的處理程式是不一樣的,而ASP.NET實際上是把這之間的差異抽象化了。問題來了,ASP.NET的設計者必須把HTML無法由超連結傳送表格的事實隱藏起來。他們的做法是在超連結的onclick產理加上幾行JavaScript程式。不過這種抽象機制也有漏洞,如果使用者關閉JavaScript功能,ASP.NET的應用程式就不能正常的運作了,萬一程式師又不瞭解ASP.NET抽象掉什麼東西,根本不可能知道出了什麼問題。
抽象滲漏法則表示,當某人發明一套神奇的新程式產生工具,可以大幅提升效率等等,就會聽到很多人說:「應該先學會如何手動進行,然後才用這個神奇的工具來節省時間。」 程式產生工具假裝抽象掉某些東西,和其他所有抽象機制一樣都有漏洞,而唯一能適當處理漏洞的方法,就是弄懂該抽象原理以及所隱藏的東西。所以抽象機制雖然替我們節省了工作的時間,不過學習的時間是省不掉的。
而這一切都似非而是地表示,即使我們擁有愈來愈高階的程式設計工具,抽象化也做得愈來愈好,要成為一個純熟的程式師卻是愈來愈難了。
我第一次去微軟實習時,寫了一個在麥金塔執行的字串程式庫。那是一個很典型的任務:寫一個自己的strcat函數傳回指向新字串結尾的指標。只要寫幾行C就夠了。我做的每件事都寫在K&R裡面(一本講C程式語言的薄書)。
今天為了要做 CityDesk,我必須會 Visual Basic、COM、ATL、C++、InnoSetup、Internet Explorer 內部機制、正規表示式、DOM、HTML、CSS 以及 XML。一大堆比古老的K&R更高階的工具,可是我還是得會K&R講的東西,否則我就完了。
我們十年前可能想像過,現在會有某些全新的程式設計典範讓程式設計更容易。事實上這些年間所建立的抽象機制,的確讓我們能處理更高複雜度的軟體開發(如GUI程式設計和網路程式設計),這是十或十五年前無法處理的。這些偉大的工具(比如OO型式的程式語言)雖然能讓我們用飛快的速度完成許多工作,不過總會有一天我們得去追查因抽象滲漏而產生的問題,到時候就得查上兩星期了。另外雖然你得雇一個以寫VB程式為主的程式師,不過單純的VB程式師是不夠的,因為當VB的抽象機制滲漏時他們就完全卡住了。
抽象滲漏法則正在拖垮我們。
這些網頁的內容為表達個人意見。
All contents Copyright © 1999-2006 by Joel Spolsky. All Rights Reserved.