最隱晦的程序設計指引

一、百家爭鳴

    俗話說,程序員半年不學新東西,就變奧特曼(out man,過時之人)了。IT行業可以說是變化最快的行業,每年都有大量的新概念、新術語、新技術被創造出來,在多數人還在一頭霧水時,“更好的”替代品又被創造出來。別的不說了,單說設計方法。

    想當年靜態類型系統是王道,誰要是搞點運行時動態綁定、用點VB什麼的,經常會被鄙視。而今動態類型語言大熱,常有人高呼Python萬歲,JavaScript身價百倍,C#也加入了動態類型支持。當年面向對象與過程式編程爭吵不休,如今大家都在盯着函數式模式,C#和C++0x都加入了函數式語言特性。當我們學面向對象時,Gang of Four(據證實,這個書名確實是四人幫的英文翻譯,故意調侃四位作者爲壞傢伙之意。)的設計模式流行起來了,當我們學Gang of Four時,其中一些模式被批爲重複、過時、不宜的,一批表達新觀點的書籍出版了(如Agile Modeling with Patterns)。當我們開始嘗試敏捷方法時,模型驅動開發(Model Driven Development)遍佈書刊封面和新聞頭條,而當我們學用Rose、Visio畫UML時,測試驅動開發(Test Driven Development)被提出來了。

    當然,歷史的發展也常常會出人意料。人們曾經認爲,動態的web必定需要Flash這樣的新解決方案,而今HTML5+JavaScript不但可以取代Flash,還可以做3D應用。人們曾經認爲,WebService需要一套新的行業標準,於是指定了SOAP等一系列協議,並歷時數年時間開發了大量WebService平臺和相關工具,而如今大家更喜歡基於基本的HTTP協議的REST方式和JavaScript本身的JSON數據協議,而且要簡單高效得多。

    但究竟什麼纔是好的設計呢?如果靜態類型系統纔算好,那麼大量優秀的動態類型語言程序算什麼?如果面向對象纔算好,那麼大量優秀個過程式、函數式程序算什麼?當年不知道匈牙利表示法的程序員就不是好coder,如今微軟的態度是,別再提了。曾經OLE的程序員可以拿到高一倍的薪水,如今誰還會提OLE。Java陣營前些年喜歡嘲笑微軟不懂Pattern,以致微軟這些年有事沒事地把Pattern掛在嘴邊,但是如今被批評得一無是處的COM(1993)卻是在Java誕生(1995)前就完美地實現了大多數的Java常用Pattern。既然COM被評價得這麼爛,業界卻搞出了一個和DCOM/COM+非常類似的CORBA。

    學過的人會說,低耦合、高內聚,如是等等。沒錯,這是一般原則,但也有情況下必須違背這些原則纔是好的設計,典型的如性能優先的情況。再比如,一般原則有說狀態查詢和修改要分開,不要寫在一個函數裏,但是遠程調用時卻經常要合併,因爲設計成網絡往返通信兩次不如一次解決。再比如,Java上有個著名的Spring Framework,可以利用反射功能,把程序中的動態選擇性初始化問題轉變成配置文件的設置,從而避免if語句(有個反if語句運動),Java程序員爲此自豪。而同樣的方法對主要用於桌面開發的.NET並不太適用,因爲沒有專業部署人員爲桌面程序用戶修改配置文件、填寫正確的類名,而且桌面程序通常要做代碼混淆(Obfuscation),會導致基於類名的反射失效。

    代碼質量管理也是近些年的新熱點,除了FxCop等傳統檢查分析工具外,計算複雜度的Cyclomatic Complexity方法和Test Coverage等量化分析得到了非常多的關注。這些工具對源碼宏觀分析提供了有效的手段。但分數高就代表代碼質量一定高嗎?尤其是對不懂技術的管理層,這些統計結果很容易成爲財務報表一樣的存在。

二、事有本末

    退一步想,當我們學自己的母語時,我們是先會說話還是先學語法呢?當我們學外語時,是先學說話還是先學語法呢?當我們讀一首詩時,我們是先感覺到好還是先用各種規則去衡量它呢?當被要求說明哪裏好時,是直接就說得出來還是要去借用各種規則來描述它的好?你是否隱約感覺到,有某種先於規則、方法存在的東西,但又難以言表,而必須用各種規則來間接地去描述它?

    有個春秋時期的故事,說齊桓公在堂上讀書,工匠輪扁在堂下幹活。輪扁問桓公說,敢問公讀的什麼書?桓公答,聖人之言。輪扁問,聖人還在嗎?桓公答,已經死了。輪扁於是說,那麼您讀的就是古人的糟粕了。桓公大怒,說,寡人讀書,輪得到你個車輪匠議論嗎!說得出道理則罷,說不出就去死!輪扁答,用臣砍造車輪來說,慢了會甘滑而不牢固,快了會苦澀而難以敲入。唯有不快不慢,得於手而應於心。其中門道我也表達不出來,也無法傳授給我兒子,所以如今七十歲了還在造輪子。聖人和他不可傳的東西都已經消失了,那麼您所讀的,不就是剩下來的糟粕了嗎?

    回到程序設計上,我們不斷創造各種各樣的方法、原則,到底是爲了什麼呢?當然是爲了寫出好程序。但到底什麼是好?說不清。但可以肯定的是,都是爲了好的性價比。性價比可以分成收益和代價。收益可以分成功能、性能、易用性、穩定性、安全性等等。代價可以分爲開發時間成本、人力成本、可維護性等等。可維護性可以分爲可讀性、可擴展性、可複用性,如是等等,以指數級細化下去。然後有各種各樣的方法,來解決各種具體的問題。如果開發時間不確定性大而卻有嚴格的deadline,那麼敏捷方法可以保證總是有個可用的發佈版本,即使可能犧牲一些功能。如果代碼只是短暫使用,或是被替代、重寫的概率很大,那麼可以完全不在乎可維護性,直接寫拋棄型代碼。如果代碼面臨高度頻繁更改的可能性,那麼用腳本語言吧。如果有個較大的開發團隊,模型設計將有效提高交流溝通的效率。如果一個程序員在一個面向對象的系統中加入了一個處理算法,他的思路和實現都是過程式的,那麼完全沒必要把它改成面向對象的,因爲那樣只會增加額外的複雜度和理解困難,而且很難說日後算法修改後這個對象設計還能適用。如是等等。

    但是,因爲這個精髓的東西難以說明,我們要麼得在不斷實踐中慢慢領悟,要麼學習別人總結出的方法來接近它。但是,人們經常糾結於方法本身。

    心都子講過一個故事,說兄弟三人在齊魯學儒術,學成回家,父親問他們,仁義之道是什麼?老大答,仁義使我愛身而後名。老二答,仁義使我殺身以成名。老三答,仁義使我身名並全。三個回答相反,卻又同出於儒,誰是誰非?你糾結了嗎?

    有句經常被引用的話:Any software problem can be solved by adding another layer of indirection. ——Steven M. Bellovin 其實原文後面還有半句:But that usually creates another problem.

    有個Java程序員寫過一篇很有趣的blog。大意說,有個人要釘個釘子掛畫框,於是去工具店買個錘子。店主說,No,我們已經不賣錘子了。錘子有很多種,大錘、拔釘錘、圓頭錘等等,如果你買了一個,之後又發現你還需要另一個怎麼辦?多數人只想要一把錘子,所以我們推出了萬能錘,可以當各種錘子使用。買者想想也是,那就買一把萬能錘吧。店主說,No,萬能錘已經被淘汰了。你想,萬能錘雖然可以當各種錘子用,但它做什麼活都沒有專門用途的錘子好使。所以,我們開始賣錘子工廠,這樣你可以隨時製造最合適的錘子。買者說,但我並不想買個工廠……店主說,沒錯,所以它已經被淘汰了。我們研究發現,不是所有的用戶都需要生產所有類型的錘子,所以我們開始賣錘子工廠設計圖,這樣用戶可以根據自己的需要定製工廠。買者說,我猜這個也淘汰了吧?店主說,沒錯,我們研究發現,用戶並不想自己建造一個工廠。於是我們推出了建造錘子工廠的工廠,來幫助用戶建造錘子工廠……

三、殊途同歸

    我們可以回想,在我們知道設計模式之前,難道我們就沒有在用這些方法嗎?之所以叫Pattern,就是因爲它至少已經被重複運用三次以上,所以我們把它單獨拿出來,賦予一個名字。所謂“新Pattern”就是一個悖論。Pattern的最大作用不是教人怎麼做,因爲面臨同樣問題時,大家多少會想出類似的解決方案;Pattern最大的作用,在於提供了一種共識、一套共通的術語,使開發人員間可以方便的交流一些複雜的系統概念。當然,有的人學了設計模式後會有眼前一亮的感覺,這是因爲切身體驗諸多問題的苦惱,突然得到解決之道,於是豁然開朗。而對有的人,沒有體驗過痛苦,只是聽說Pattern是好的,於是就開始在代碼裏羅列Pattern。

    同理的,雲計算是什麼全新的東西嗎?WebService是什麼全新的東西嗎?之前就沒有類似的問題需要解決,就沒有類似的解決方案嗎?如果讓你穿越到它們誕生前,從頭解決這些分佈式計算的問題,你會不會做出一個類似的解決方案?那麼,又是什麼使你能夠發明這些新東西呢?是不是基本技能在特定問題下的一次應用呢?

    不管用哪種途徑,用這套方法也好,那套方法也好,只靠自己琢磨也好,走對的話,都是通往同一條大道。不要把自己圈在一個小圈子裏,圈子越小,越容易偏執。站得越高,看得越遠,眼前的景色越壯麗,所謂的是非對錯,不過是遠處的一點而已。

    但是但是,現實中,不是所有的問題都是能由開發人員解決的,也不是所有的人都想要解決問題。流程、方法、思想、工具等,不管看起來再好,在不想解決問題的人面前,連個屁都不算。一個項目決定時的天時地利人和,已經八九成決定了項目的結局。當然,怨天尤人也沒用,上天總是會給人幾次機會的,機會到來時能不能抓得住,就看個人修爲了。
發佈了118 篇原創文章 · 獲贊 137 · 訪問量 50萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章