2010年6月21日 星期一

軟體設計的Anti-Pattern:瑞士刀

Anti-Pattern:追求泛用性而不計所造成的overhead代價

我愛瑞士刀,這真是個很棒的發明。有太多種工具我們不常用,不過需要時沒有它還真麻煩。在家中可以準備一個工具箱隨時應急,但是出門在外總不好隨時拎著工具箱。當年去歐洲讀書,父親送我一副瑞士刀。剛開始住宿還沒安定下來的期間,從切水果到鎖緊眼鏡螺絲,一切都靠它。

軟體設計,常在簡單性與泛用性之間做掙扎。一套Library或是Framework,因為簡單好用,使用的開發人員就漸漸增加。這套工具要適用的不同應用情境與軟硬體環境就越來越多,需要逐漸調整,做些擴充以及一般化,簡單的工具就會變複雜一些。持續演化與累積,最後複雜度會變得很可觀。這個複雜度最常反應在設定檔,或是物件的起始方法,以及函式的呼叫方法

瑞士刀做得再好,再萬能,卻不會是專業匠師的工具。沒有餐廳的廚師是用瑞士刀切菜的,即使這個刀鋒是出自瑞士的頂級工藝。木工不會用瑞士刀鋸木頭,也不會用它拴螺絲。因為瑞士刀的握把不順手、尺寸太小、刀身太薄等等原因,只適合暫時用,而不能取代正式的工具。

原先只是一個簡單好用的軟體工具,簡單的constructor就能呼叫出來用。現在為了適用於各種不同情境,變成要在設定檔指定好各種參數及模組位置,透過一個Factory Method來initialize一個context物件,再由這個context物件來建出你要的物件。呼叫method的方法也越來越複雜。這個工具是變得宇宙無敵強了,但我們如果只是為了一個簡單的功能,為什麼需要費神去學一個這麼複雜的API,用這麼不直覺的方法來呼叫它呢?

每年各種發明大展,"多用途"似乎永遠是個發明的主題,這種發明也許非常巧妙有趣,但是很難造成革命性的影響。因為它在擔任單一功能的時候,並沒有專用的工具好用。所以只能適用在特殊場合。例如旅行時,或是空間很有限,或者是罕用的工具。

有種檢視軟體物件模型優劣的方法,叫做CRC Cards。指定一組user stories,開發人員先想出各種不同的模型,寫在卡片上。然後預想未來需求可能有哪些變更,use stories有可能會有哪些變化。再逐一檢視各組卡片,比較哪個模型總能在最小的調整下,應付最多的變化。

多年前在一家公司擔任研發主管時,第一次試著引進CRC Cards,希望將產品核心的API改得更加物件導向。在進行之前,開發人員似乎就有默契,傾向某個設計。可能是因為翻修的幅度會最小。那個模型太不直覺又太複雜了,我當時希望新模型能有更徹底的簡化。表面上保持主管的中立,其實心裡有信心,CRC Cards的測試必將淘汰那個太複雜的模型。

沒想到那個複雜模型安然通過了全部的測試,幾乎不用做什麼變動就可以應付各種的需求變更。其實是因為翻修自家公司的產品,工程師們早就熟悉所有需求的變化,預先將所有擴充功能放在模型中,所以讓模型過於複雜。但它絕不是最自然的設計。當時缺乏經驗的我只覺得不太對勁,張目結舌,無話可說。主管必須遵守自己訂的規矩。這套產品就一直有個難懂的核心API。

今天的我已經了解,當時描述CRC Cards的"好模型規則"是有缺陷的,這個缺陷會鼓勵複雜設計以及過度設計。因為只要你的模型是把多功能的瑞士刀,這個模型一定會一枝獨秀地獲選。即使這把瑞士刀長這樣也沒問題:

附圖是Wegner的Giant Knife,全世界最強的瑞士刀。

這樣的工具問題在於,雖然什麼都能做,但是削蘋果皮一定很不順手。它的確什麼功能都有,但是你只是要開個罐頭也要花個十分鐘來找出合適的開罐器來。

好模型規則應該加個但書,修正為:
最好的模型,看似針對目前需求所量身訂做的極簡設計,但對於可預見的需求變更,卻可以透過簡單的擴充而滿足。

也就是說,提高設計的泛用性,不該降低簡單應用情境下的便利性,也不該提高常用功能的複雜度。

當然,這個規則太嚴格了,有時簡單性與泛用性不能兩全。這種時候就要記得,不要一昧追求泛用性。增加泛用性雖然符合"元件化"、"重用性"的理想,卻也可能帶來不必要的複雜度。要小心衡量值不值得,或是用各種方法避開那個複雜度。例如Convention over configuration就是一種很好的設計思維。常見的設計問題是,太急於追求泛用性(希望大家都用我的工具)而輕易捨棄極簡風。

如果要使用一套包山包海的工具,我為泛用性付出的價格,可能超出共用原始碼的效益。因為我必須先去學習如何使用這套API(而瑞士刀至少不用學就會用)。以後別人維護我的程式碼,也要先了解這套API。表面上重用性高,可是反而提昇了維護的門檻。(誰知道為了轉換個日期的util,我還要在設定檔指定葛利果曆,以及關閉夏令節約時間。)

除了學習成本的問題以外,其實程式碼本身也可能變得很囉嗦。雖然將重複的程式碼共用,會有簡化,但是為了迎合泛用性,也會複雜化。偏偏被簡化掉的,重複的程式碼比較容易懂,而為泛用性而加的程式碼卻要研究過才能懂,變成得不償失。說不定跳過這個工具,自己實作個簡單的版本會更快一些,也好維護。

2 則留言:

喵尾巴 提到...

就我自己的觀察,工程師一般來說似乎愛好複雜的東西,但是痛恨重複的code(這也包括我自己在內!),所以極簡設計個人覺得是有蠻高門檻的耶...

EC 提到...

設計者喜歡設計偉大的東西,不過也懶得學別人設計的東西,尤其是別人的風格跟自己不對味的時候。至少我就是如此。所以常要提醒自己將心比心。

我覺得很多時候並不是有了泛用性就一定得犧牲簡單性。問題常出在設計者只一昧擴大泛用性,不考慮簡單性的代價。

泛用化只要適可而止,就可以避免它成為大怪物。