天天看點

設計:小即是美

博爾赫斯說:“寫散文體的短文——寓言、神話、短故事——給了我某種神秘的滿足。想起這些篇章,就仿佛想到硬币:實在、結實、閃光的小物體,更多的東西的樣品。”顯然,小物體之美,讓博爾赫斯着迷。

同樣,在軟體設計領域裡,小的設計同樣讓我着迷。這裡所謂的“小”,并非絕對的小,而是強調一種恰如其分的設計哲學。在開發過程中,每一次疊代的目标不宜設立過大,需小步前行,避免過度設計。在設計開發時,整個系統最好由松散耦合的細小子產品組成。這些細小子產品由于功能相對獨立而單一,因而更易于了解。

設計:小即是美

                                                                                       <b>dennis ritchie</b>

在設計系統架構時,我們要注意克制做大做全的貪婪野心,盡力保證系統的小規模。unix的締造者之一dennis ritchie就曾遭受過将系統做大做全的滑鐵盧。他在貝爾實驗室的第一個任務,是參與大項目multics,即開發一個前所未有的、可以多人使用的、同時運作多個程式的作業系統。該項目由貝爾實驗室、麻省理工學院和通用電氣公司三方聯合研制,但是由于設計過于複雜,遲遲拿不出成果,1969年貝爾實驗室宣布退出。

痛定思痛,dennis ritchie和同僚ken thompson之後在設計unix時,就吸取了multics設計複雜而導緻失敗的教訓,提出了"保持簡單和直接"(keep it simple stupid)的原則,即所謂kiss原則。

遵循kiss原則,整個unix系統由許多小程式組成,每個小程式隻能完成一個功能,任何複雜的操作都必須分解成一些基本步驟,由這些小程式逐一完成,再組合起來得到最終結果。表面上看,運作一連串小程式很低效。但是事實證明,由于小程式之間可以像積木一樣自由組合,是以非常靈活,能夠輕易完成大量意想不到的任務。而且,計算機硬體的更新速度非常快,是以性能也不是一個問題。另一方面,當把大程式分解成單一目的的小程式,開發變得容易,unix在短短幾個月内就問世。

我們千萬不要輕視小的力量。

專注的小可以保證獨立進化。

從“自治”思想看,它需要實作一定程度的自給自足,并保證對外互動的接口足夠穩定。這符合老子的“大國小民”的思想。

這種專注的小首要在于分離職責。一種分離的角度是以“内外之分”觀察核心與邊緣的職責,然後以子產品分離的方式分别為它們尋找到“安身立命之所”。在架構設計上,為保證架構的小,我們常常采用這樣的觀察視角,這就是所謂的“核心模式”,通過識别出整個架構的核心功能,以此作為整個架構的基礎,而其餘功能皆可視為架構的外圍功能,根據關注的域對其進行切分。這些外圍功能互相之間應盡量減少耦合,使其能夠獨立進化。

設計:小即是美

<b>                                                                                  核心:即多個集合的共同交集</b>

spring架構的設計正是遵循了這樣的設計理念。除了spring ioc是整個架構必備的元件之外,spring mvc、spring batch job、spring data等之間沒有依賴關系,可以根據項目自身情況酌情裁剪。

那麼,如何才能保證設計的系統足夠小?首先,在設計思想上要确立“小即是美”的美學觀,要清晰地辨識且能夠欣賞小的靈活之美,完整之美與輕盈之美。隻有在思想上認同它,你才能順勢而為;隻有從心理上感受到這種美麗,你才能響應它的召喚。

靈活之美,在于它能快速地響應變化,這種變化可能是局部的,也可以是整個設計方向的改變。例如,在多數企業系統和網際網路系統中,都需要分離online和offline任務,以指定不同的架構決策。又例如,我們可以設計獨立的、具有最小功能子集的batch job來承擔背景任務。這些batch job可以作為一個單獨的應用程式執行在單獨的程序中。一旦需求要求我們對設計做出改變,我們也能将修改控制在足夠小的範圍中,進而保證對整個系統不會帶來巨大的影響。

若遵循eda(event driven architecture)模式,我們可以根據業務領域的不同,設計出功能最小完整的自治元件。元件之間的通信通過事件來傳播,利用釋出者/訂閱者的方式解除元件之間的耦合;又或者利用消息傳遞來處理業務邏輯,例如在akka中,我們可以設計出靈活而小的actor對象;微服務(micro service)架構則從服務級别展現了設計的靈活之美。

輕盈之美,展現在它的功能并不臃腫,對外部的依賴較少,既容易在系統中快速引入,又不會使原有系統變得笨重,還能很友善地部署或者啟動。

展現了輕盈之美的元件往往具有良好的可測試性。我們可以利用六邊形架構将系統分隔為内、外兩個邊界,凡是系統對外的通信,皆通過端口(port)和擴充卡(adapter)完成,這樣就能較好地解除對外部環境的依賴,提高系統的可測試性。而清晰的邊界劃分也是設計小元件的一種有效手段。

設計:小即是美

<b>                                                                            六邊形架構:port-adapter模式</b>

若對于架構或平台而言,則需要盡力降低架構或平台的侵入性。當年rod jonson之是以提出j2ee without ejb,正是因為ejb的侵入性帶來了諸多病症。當然,從另一個角度來講,我們自己研發的産品或項目也要盡可能擺脫對外部資源的依賴,即所謂“穩定依賴原則”。robert martin提出的clean architecture清晰地勾勒出這樣的思想。在clean architecture的表述中,他讓外部易變的部分依賴于更加穩定的部分,如域模型,而非形成相反的依賴關系。這樣還可使實作變得更易于變化;多變的部分依賴于穩定的部分。好架構就要能輕松地改變那些易變的決定。

設計:小即是美

<b>                                                                         clean architecture</b>

完整之美,在于它是自足的。完整并不意味着大而全,而在于它足夠精簡,沒有備援。當然,它同時應該是沒有殘缺的。殘缺,意味着它無法在沒有外部支援的情況下,完成自己應該完成的工作。這種美感符合“麻雀雖小,五髒俱全”的标準。standalone的微服務,正好展現了這種自容器的完整之美。

小的益處還有一點,它可以使得我們在架構決策或技術選型時,可以變得更加從容。

譬如說,因為某些原因我們需要将整個企業系統(monolithic架構)從weblogic上遷移到jboss上,無疑,這是一個艱難的決定,實施起來更是一個漫長的過程。如果系統是基于micro service的架構風格進行建構,每個服務根據各自情形選擇自己的技術棧。倘若需要對某些服務進行技術棧遷移,相信這個問題不再變得棘手。——大象可以輕盈地跳舞,但付出的努力會百倍于一隻靈活的狐狸。

如今,java已經發展到java 8,引入的lambda表達式等多個特性如此鮮嫩,讓人垂涎不已。然而據我所知,國内多數企業的java項目仍然停滞在jdk 6裹足不前。是jdk 8不夠好嗎?非也。蓋因為求穩的他們仍然心存顧慮。即使oracle号稱這種jdk的遷移多麼的平滑,多麼的穩健,多數企業仍然不敢輕易做出遷移的決定。若因為遷移而帶來未知的缺陷,可謂得不償失。既然現在項目運作良好,何必冒此風險。

于是,我們這個行業因為系統的龐大而變得守舊老成,亦步亦趨。并非大家沒有冒險的精神,實則是龐大的項目難以靈活地改變方向。倘若隻是更新系統中的某一個庫或架構,形勢就截然不同了。記得在沒有lambda的時代,當我們讓客戶看到了guava的好處時,要引入guava就輕而易舉,真如順水推舟了。

當我們發現某些功能具備獨立和專注的特征時,都是可能做出小系統的機會。這些小系統并不一定是子系統或子產品,它還可以是一個獨立的應用或服務。

例如在一個稅務系統中,需要生成複雜的稅務報表。它的整個邏輯是相對獨立的,不管是報表的動态生成,格式的轉換,資料的查詢以及流的處理和pdf文檔的生成,都與系統其他部分關聯不大。唯一可能與系統存在緊密關聯的是資料庫。但為了解決高峰期的性能問題,我們可以建立單獨的資料提取器,又或引入流處理,定期将資料提取出來,放入記憶體資料庫中。将這樣相對獨立的功能做成服務,就能夠獨立演化,并有效支援服務請求的可伸縮。這樣的小型服務可以更靈活地應對變化。當我們發現記憶體資料庫不能滿足大量請求時,也可以輕而易舉地将其遷移到nosql上,并根據資料的屬性例如按照地域進行分區,支援水準擴充。

若要保證系統的小,我們還可以嘗試使用腳本。在開發軟體系統時,可以使用一些腳本語言來開發一些小工具,以應對靈活的需求變化,消除重複代碼,實作某些步驟的自動化。例如用groovy編寫一些函數,用ruby編寫代碼生成工具,又或者使用gradle、sbt實作系統的自動部署,啟動伺服器等腳本。腳本語言具有很好的靈活性,而動态語言的特性也使得我們能夠編寫出短小精悍的超級小工具,甚至可以作為系統子產品之間的粘合劑,如機器齒輪上的潤滑油一般,讓整個系統充滿活力。

russ miles認為:團隊的開發速度經常因編寫代碼體積和複雜度的增長而放緩。他認為元件結構在簡化架構方面非常重要,并提出了life-preserver模型。這是一個環形結構,所有的基礎設施軟體都在環上處理內建,而核心業務元件在環内加入業務價值。這種模型的核心是利用事件來簡化架構。

使用事件的一個顯著特點是解耦,使得事件的釋出者與訂閱者都可以獨立演化,也可以自由增加事件的訂閱者數量。我們可以将事件的釋出者與訂閱者設計為獨立的小程式,就像unix系統中那些小工具一樣,通過管道建立關聯。這樣的設計思想稱之為eda(event driven architecture)。

當然,要做到系統的“小”,必然也是要付出代價的。奧卡姆剃刀定律認為:“若無必要,勿增實體”。剖析kent beck提出的“簡單設計”原則,在滿足了客戶功能、無多餘重複、清晰表達設計意圖的前提下,需要遵循奧卡姆剃刀定律。蓋因為系統分解得約小,就會因為“實體”數量的增加,引入額外的複雜度,包括對實體的管理、實體之間的協作等。顯然,任何設計決策都有其兩面性,我們需要放入到當下的上下文(context)中做出正确的判斷。正如愛因斯坦所說:“讓它盡可能簡單,但不要過于簡單。”看來,我們對任何事情都需要把握一個“度”,水滿則溢,月盈則虧,故而需要損有餘而補不足。

文/<b>逸見(簡書作者) </b>張逸

著作權歸作者所有,轉載請聯系作者獲得授權,并标注“簡書作者”。