張逸:運用跨界思想體悟軟件設計
設計或者是一種解謎,世界就在眼前,然而它卻是未知的,神秘的,我們試圖通過分析建立一個普適的模型,去解構這個已經存在的世界。這個謎題如此引人入勝,它沒有標準答案,卻又真實的呈現在我們面前。
設計就好像是研究電信號頻譜結構的頻譜分析儀,通過掃描和偵測信號,再利用我們已經充分證明的算法例如快速傅里葉變換(FFT)去獲得頻譜分布圖,然后工程師就可以借助經驗與頻譜知識甄別信號特征,從而判斷電子設備是否出現異常。這種設計方式,我稱之為“分析式設計”。
分析式設計
分析式設計常常起源于對研究領域的假想與猜測,進而對海量的信息進行篩選和甄別,提煉出對解決該問題有幫助的信息,再利用設計經驗去推導合理的設計模型。人類對于宇宙的探索,就是這樣的分析式設計。在史蒂芬.霍金的著作《時間簡史》中談到了古希臘哲學家亞里士多德對地球形狀的論證。
早在公元前 340 年,希臘哲學家亞里士多德在他的《論天》一書中,就能夠對于地球是一個圓球而不是一個平板這個信念提出兩個有力的論證。
第一,他意識到,月食是由于地球運行到太陽與月亮之間引起的。地球在月亮上的影子總是圓的,這只有在地球本身為球形的前提下才成立。如果地球是一塊平坦的圓盤,除非月食總是發生在太陽正好位于這個圓盤中心的正下方的時刻,否則地球的影子就會被拉長而成為橢圓形。
第二,希臘人從旅行中知道,在南方觀測北極星,比在較北地區,北極星在天空中顯得較低。
亞里士多德采用的兩個論證就是他利用智慧與知識篩選出來的有用信息,這有利于他建立“地球是一個圓球”的天體模型。
這種分析式設計常常體現為一種漸進的設計方式,原因在于我們總是以局部去推測整體,就難免陷入一葉障目的困境。從亞里士多德到托勒密的設想,再到哥白尼的日心說,開普勒的天體模型,牛頓的萬有引力定律,直至愛因斯坦的相對論,整體天體模型都在不斷的批謬中披荊斬棘,艱難發展,并逐漸趨向事實的真理。然而無論正繆,這些哲學與思想先驅們都是在對靜默存在的宇宙進行的一種分析,通過把握某種內在的如脈搏跳動的規律,設計為嚴謹的模型。
在軟件領域,諸多問題已經現實存在,但這種存在如沙礫藏身于溪流,奇石隱匿于幽谷,沒有敏銳的洞察力,很難明察問題的真相。設計者需要對繁雜紛亂的需求進行梳理和甄別,分析軟件目標或愿景,逐步建立模型,并通過原型或真實實現去驗證其模型,根據反饋的結果進行改進、打磨,或增或刪,以求最簡的方式滿足需求,從而獲得良好的解決方案去指導具體的實現。
例如在大數據處理領域,我們面臨的問題是數據的海量與性能之間的博弈,非結構性關系與分析結果之間隱藏的線索,還有諸如因果推論、分布式存儲、實時處理等多個問題雜糅在一起,就構筑了一個復雜的外部世界。我們需要去深入探索數據分析的問題域,挖掘數據與數據之間的隱含關系,架構或重現各種復雜多變的應用場景,小心求證,實驗探索,才能分析獲得以當前場景而論為最優的解決方案,得出設計模型。
分析還可以逐步添加約束的方式來推導設計。Roy Thomas Fielding 博士在推導 REST 架構風格時,正是采用了這種方式。在 Fielding 的論文《架構風格與基于網絡的軟件架構設計》,他寫道:
Web 架構背后的設計基本原理,能夠被描述為由一組應用于架構中元素之上的約束組成的架構風格。當將每個約束添加到進化中的風格時,會產生一些影響。通過檢查這些影響,我們就能夠識別出 Web 的約束所導致的屬性。然后就能夠應用額外的約束來形成一種新的架構風格,這種風格能夠更好地反映出現代 Web 架構所期待的屬性。
Fielding 從一個沒有任何約束的“空”風格開始,通過逐步施加分離關注點、通信無狀態、緩存、統一接口、分層、定制化等約束,整個架構就像洞穴中的原上猿逐步進化為人類一般,從“空”風格的混沌架構演化為最終的 REST 風格。相較于直接設計一個嚴謹的模型,這種逐步添加約束的方式更加自然流暢,更符合人類的知識積累模式。設計軟件時,我們可以通過識別系統的風險,驅動我們去施加約束,然后漸進地形成針對性的解決方案,以降低風險。這即為 George Fairbanks 提出的風險驅動模型。
創造式設計
設計或者是一種創造,無中生有地將一個事物美輪美奐地創造出來。此時,設計者就像是造物主,他要設計的軟件就是一個獨立的星球,可以按照他認為最好的方式去設計星球的風景,安排萬物遵循生長的秩序。即使是從無到有創造出一顆星球,依舊可以從亙古存在的遙遠星球獲得靈感,作為參考物或原型幫助新的設計。我將這種設計方式稱為“創造式設計”,它考察的是設計者的經驗、巧思與創意。
作家在寫作時,整個構思的過程就是一種創造式設計。偶然經歷的一件事情可能會在內心觸動作家創作的神經,以此為靈感作為創造的契機,然后逐漸豐富,按照作品人物的性格、職業以及身處的環境開始推動情節的發展。這種創造未必是“硬”推出來的,相反,它可能極為自然流暢,如蘇東坡云:“大略如行云流水,初無定質,但常行于所當行,常止于所不可不止,文理自然,姿態橫生。”
創造式設計雖為無中生有,但并非徹底的無,可能是一種發現。斯蒂芬.金在接受《紐約客》的一次采訪時,說道:
我對采訪我的馬克.辛格說,我相信故事猶如埋在地下的化石,是被人們發掘出來的,他說他不相信我的話。我回答說這沒關系,只要他相信我這么相信就夠了。我確實這么認為。故事不是紀念T恤衫或是掌上游戲機,它們是遺跡,屬于一個未被發現但已經存在的世界。作家的工作就是利用他/她工具箱里的工具把每個故事盡量完好無損地從地里挖出來。有時候你發掘的化石很小,可能只是顆貝殼。有時候很巨大,是頭骨架龐大牙齒凌厲的霸王龍。不論哪種,短篇小說或是一千多頁的小說巨著,挖掘的技術大致上相同。
創造式的設計在一開始可能并不明確它所要創造的目標,而是像斯蒂芬.金的方式如發掘遺跡一般慢慢將泥土剔除,逐漸顯現。這會讓人產生不經意創造出來的感覺。Spring 之父 Rod Johnson 從一開始就不是要創造一個框架,他是在 J2EE 項目中通過對 EJB 的反思逐步演化出來的 Spring。這種創造其實是一種“循證式創造”,它遵循的是應用場景,而非不切實際的空想。
2004 年,當哥本哈根商學院的大三學生 David Heinemeier Hansson(人稱 DHH)接到 37signals 公司電話時,不會有人想到這會是 Ruby On Rails 誕生的導火線。顯然 RoR 并非 DHH 有意創造出來的,它只是為了打造 Basecamp 產品而自然而然衍生出來的框架。就連 DHH 創造用的工具也經歷了變更,從最初選擇的 PHP,轉向了更靈活、更簡單的 Ruby。正是 Ruby 對元數據編程的超強支持,Rails 才能做到如此的精簡。
創造式設計出來的產品常常凸顯了設計者的意圖與態度,就仿佛上帝的意志。這種態度徹頭徹尾地體現了設計者的善惡論,甚至可以說是一種信仰。如果將設計者視為產品的上帝,則這種設計意圖與態度就是一種自然法。正如中世紀的神學家阿奎那所說:“自然法已不再是最高的法,而且巧妙地將自然法與上帝的永恒法結合在一起,認為我們賴以辨別善惡的自然理性之光,即自然法,不外乎是神的榮光在我們身上留下的痕跡”。
在編程語言的設計中,尤其體現了這種意志與法規的結合。Go 語言的設計者就對在面向對象領域中繼承的濫用表達了足夠的憂慮和擔心。Rob Pike 在《Go 在谷歌:以軟件工程為目的的語言設計》提及 Go 語言設計者對類型結構的考慮:
類型層次結構這種模型會促成早期的過度設計,因為程序員要盡力對軟件可能需要的各種可能的用法進行預測,不斷地為了避免掛一漏萬,不斷的增加類型和抽象的層次。這種做法有點顛倒了,系統各個部分之間交互的方式本應該隨著系統的發展而做出相應的改變,而不應該在一開始就固定下來。
因而,在 Go 語言中并未提供繼承的特性,而是利用接口(包括對 Duke Type 的支持)來支持 OO 中的多態,而以組合來保證邏輯的復用。
設計態度甚至體現為設計者對所要創造的世界所持有的世界觀。面向對象與函數編程陣營就是兩種截然不同的世界觀。面向對象是名詞的世界,而函數式的思想則以動詞為準,從而將函數提升為一等公民。觀察世界的角度不同,設計的途徑也就不同。OO 的模式與原則在函數式語言中,幾乎都可以用函數來表示。Scott Wlaschin 在演講 Functional Design Patterns 中比較了這兩種不同的編程范式:
設計的演進
無論是分析式設計,還是創造式設計,目的都是為了要解決問題,即針對問題域(Problem Domain)尋求解決方案(Solution)。問題域可大可小,設計可復雜可簡單。設計者往往難以一蹴而就獲得最佳的解決方案,而需要在不斷的演化中改進設計。2004 年,Martin Fowler 撰寫文章《Is Design Dead》,將設計劃分為計劃式設計和演進式設計(Planned and Evolutionary Design)。我認為,這二種設計方式都需要遵循設計的基本原則,區別只在于設計的目標。演進的設計提倡滿足客戶現有的需求;而計劃的設計則需要考慮未來的功能擴展。演進的設計推崇盡快地實現,追求快速確定解決方案,快速編碼以及快速實現;而計劃的設計則需要考慮計劃的周密性,架構的完整性并保證開發過程的有條不紊。
Neal Ford 提出的 Emergent Design 實則就是一種演進式設計,在他撰寫的系列文章中,開篇就提出:“演化架構(evolutionary architecture)和緊急設計(emergent design)都是將重要的決策推遲到最后責任時刻(Last Responsible Moment)的敏捷技術”。他建議,通過工具獲得質量指標,并利用測試驅動設計追求簡單設計,進而對設計進行重構,在重構過程中積累慣用模式來指導重構的方向,從而完成架構的演進。
George Fairbanks 認為除了計劃式設計和演進式設計之外,還包含第三種設計方式,稱之為最小計劃式設計(Minimal planned design),或可稱為是中庸之道的選擇。書中認為,“演進式設計需要與一些敏捷實踐配合,包括重構、測試驅動設計與持續集成。而計劃式設計背后隱藏的思想是在構造開始之前,制訂的計劃可以設計出很好的細節。”至于最小計劃式設計,則介乎于演進式設計與計劃式設計之間。支持這種設計的人認為:如果完全采取演進式設計,可能會使得設計走向死胡同;而計劃式設計又非常難,因為事先對系統并沒有全面的了解,可能導致設計錯誤。
在 2002 年 Bill Venners 對 Martin Fowler 的采訪中,Martin Fowler 認為,最合理的分配是 20% 的計劃式設計,80% 的演進式設計。George 認為需要權衡計劃式與演進式設計。一種做法是在項目初期進行計劃式設計,確保架構能夠處理最大的風險。之后,就可以通過局部的設計來應對需求的變化,或者采用演進式設計,通過推行重構、測試驅動設計與持續集成對架構進行演化。
整體而言,這三種方式的設計各有優劣,我們應根據具體的場景,具體的項目,具體的團隊進行針對性地分析。應該把握“因地制宜”的原則,認識到不同的項目需要不同的設計方式。對于不同的開發團隊,做出的選擇也會不同。例如,如果開發團隊精于重構、測試驅動設計,并能很好地實施持續集成,就可以考慮采用演進式設計或最小計劃設計。
顯然,無論采取何種設計方式,我們都應該追求恰如其分,而非臻至完美。設計之美需要以演進的方式逐漸打磨而成,它需要保持簡單,避免重復,擁抱變化。故而要保證設計的清晰意圖表達,干脆利落,善于留白來保證虛實相間,簡約平衡,尋求整體的美感。