架構(gòu)設(shè)計中的方法學(xué)(5)——簡單設(shè)計

字號:

XP非常強調(diào)簡單的設(shè)計原則:能夠用數(shù)組實現(xiàn)的功能決不用鏈表。在其它Agile方法中,簡單的原則也被反復(fù)的強調(diào)。在這篇文章,我們就對簡單性做一個全面的了解。
    架構(gòu)應(yīng)該設(shè)計到什么程度?
    軟件的架構(gòu)都是非常的復(fù)雜的,帶有大量的文檔和圖表。開發(fā)人員花在理解架構(gòu)本身上的時間甚至超出了實現(xiàn)架構(gòu)的時間。在前面的文章中,我們提到了一些反對象牙塔式架構(gòu)的一個原因,而其中的一個原因就是象牙塔式架構(gòu)的設(shè)計者往往在設(shè)計時參雜進過多的自身經(jīng)驗,而不是嚴(yán)格的按照需求來進行設(shè)計。
    在軟件開發(fā)領(lǐng)域,最為常見的設(shè)計就是"Code and Fix"方式的設(shè)計,設(shè)計隨著軟件開發(fā)過程而增長?;蛘?,我們可以認為這種方式根本就不能算是設(shè)計,它抱著一種船到橋頭自然直的態(tài)度,可是在設(shè)計不斷改動之后,代碼變得臃腫且難以理解,到處充滿著重復(fù)的代碼。這樣的情形下,架構(gòu)的設(shè)計也就無從談起,軟件就像是在風(fēng)雨中的破屋,瀕臨倒塌。
    針對于這種情形,新的設(shè)計方式又出現(xiàn)了,Martin Fowler稱這種方式為"Planned Design"。和建筑的設(shè)計類似,它強調(diào)在編碼之前進行嚴(yán)格的設(shè)計。這也就是我們在團隊設(shè)計中談到的架構(gòu)設(shè)計師的典型做法。設(shè)計師們通常不會去編程,理由是在土木工程中,你不可能看到一位設(shè)計師還要砌磚頭。
    "Planned Design"較之"Code and Fix"進步了許多,但是還是會存在很多問題。除了在團隊設(shè)計中我們談的問題之外,需求變更將會導(dǎo)致更大的麻煩。因此,我們理所當(dāng)然的想到進行"彈性設(shè)計":彈性的設(shè)計能夠滿足需求的變更。而彈性的設(shè)計所付出的代價就是復(fù)雜的設(shè)計。
    題外話:
    這里我們談?wù)?Planned Design"引出的一些問題,并沒有任何排斥這種方式的意思。"Planned Design"還是有很多可取之處的,但也有很多需要改進的地方。事實上,本文中我們討論的架構(gòu)設(shè)計方式,本質(zhì)上也是屬于"Planned Design"方式。和"Planned Design"相對應(yīng)的方式是XP所主張的"Evolutionary Design"方式,但是這種方式還有待于實踐的檢驗,并不能簡單的說他就一定要比"Planned Design"先進或落后。但可以肯定的一點是:"Evolutionary Design"方式中有很多的思想和技巧是值得"Planned Design"借鑒的。
    解決方法:
    XP中有兩個非常響亮的口號:"Do The Simplest Thing that Could Possibly Work"和"You Aren't Going to Need It"(通常稱之為YAGNI)。他們的核心思想就是不要為了考慮將來,把目前并不需要的功能加到軟件中來。
    粗看之下,會有很多開發(fā)人員認為這是不切實際的口號。我能理解這種想法,其實,在我熱衷于模式、可重用組件技術(shù)的時候,我對XP提倡的簡單的口號嗤之以鼻。但在實際中,我的一些軟件因為復(fù)雜設(shè)計導(dǎo)致開發(fā)成本上升的時候,我重新思考這個問題,發(fā)現(xiàn)簡單的設(shè)計是有道理的。
    降低開發(fā)的成本
    不論是模式,可重用組件,或是框架技術(shù),目的都是為了降低開發(fā)的成本。但是他們的方式是先進行大量的投入,然后再節(jié)省后續(xù)的開發(fā)成本。因此,架構(gòu)設(shè)計方面的很多思路都是圍繞著這種想法展開的,這可能也是導(dǎo)致開發(fā)人員普遍認為架構(gòu)設(shè)計高不可攀的原因。
    XP的方式恰恰相反,在處理第一個問題的時候,不必要也不可能就設(shè)計出具有彈性、近乎完美的架構(gòu)來。這項工作應(yīng)該是隨著開發(fā)的演進,慢慢成熟起來的。我不敢說這種方式肯定正確,但是如果我們把生物的結(jié)構(gòu)視同為架構(gòu),這種方式不是很類似于自然界中生物的進化方式嗎?
    在一開始就制作出完美的架構(gòu)的設(shè)想并沒有錯,關(guān)鍵是很難做到這一點??偸菚泻芏嗟膯栴}是你在做設(shè)計時沒有考慮到的。這樣,當(dāng)一開始花費大量精力設(shè)計出的"完美無缺"的架構(gòu)必然會遇到意想不到的問題,這時候,復(fù)雜的架構(gòu)反而會影響到設(shè)計的改進,導(dǎo)致開發(fā)成本的上升。這就好比如果方向錯了,交通工具再快,反而導(dǎo)致錯誤的快速擴大。Martin Fowler在他的論文中說,"Working on the wrong solution early is even more wasteful than working on the right solution early"(提前做一件錯事要比提前做一件對的事更浪費時間),相信也是這個道理。
    更有意思的是,通常我們更有可能做錯。在我們進行架構(gòu)設(shè)計的時候,我們不可能完全取得詳細的需求。事實上,就算你已經(jīng)取得了完整的需求,也有可能發(fā)生變化。這種情況下做出的架構(gòu)設(shè)計是不可能不出錯的。這樣,浪費大量的時間在初始階段設(shè)計不可能達到的"完美架構(gòu)",倒不如把時間花在后續(xù)的改進上。
    提升溝通的效率
    我們在團隊設(shè)計中已經(jīng)談過了團隊設(shè)計的目標(biāo)之一就是為了降低溝通的成本,以期讓所有人都能夠理解架構(gòu)。但是如果架構(gòu)如果過于復(fù)雜,將會重新導(dǎo)致溝通成本的上升,而且,這個成本并不會隨著項目進行而降低,反而會因為上面我們提到的遇到新的問題導(dǎo)致溝通成本的持續(xù)上升。
    簡單的架構(gòu)設(shè)計可以加快開發(fā)團隊理解架構(gòu)的速度。我們可以通過兩種方式來理解簡單的含義。首先,簡單意味著問題的解不會非常的復(fù)雜,架構(gòu)是解決需求的關(guān)鍵,無論需求再怎么復(fù)雜多變,總是可以找出簡單穩(wěn)定的部分,我們可以把這個簡單穩(wěn)定的部分做為基礎(chǔ),再根據(jù)需要進行改進擴展,以解決復(fù)雜的問題。在示例中,我們提到了measurement pattern,它就是按照這種想法來進行設(shè)計的。
    其次,簡單性還體現(xiàn)在表示的簡單上。一份5頁的文檔就能夠表達清楚的架構(gòu)設(shè)計為什么要花費50頁呢?同樣的道理,能夠用一副簡單的圖形就能夠表示的架構(gòu)設(shè)計也沒有必要使用文檔。畢竟,面對面的溝通才是率的溝通,文檔不論如何的復(fù)雜,都不能被完全理解,而且,復(fù)雜的文檔,維護起來也需要花費大量的時間。只有在兩種情況下,我們提倡使用復(fù)雜的文檔:一是開發(fā)團隊沒有辦法做到面對面溝通;二是開發(fā)成果要作為團隊的知識積累起來,為下一次開發(fā)所用。
    考慮未來
    我們之所以考慮未來,主要的原因就是需求的不穩(wěn)定。因此,我們?nèi)绻紤]未來可能發(fā)生的需求變化,就會不知覺的在架構(gòu)設(shè)計中增加復(fù)雜的成分。這違背的簡單的精神。但是,如果你不考慮可能出現(xiàn)的情況,那些和目前設(shè)計格格不入的改變,將會導(dǎo)致大量的返工。
    還記得YAGNI嗎?原則上,我們?nèi)匀粓猿植灰诂F(xiàn)有的系統(tǒng)中為將來可能的情況進行設(shè)計。但是,我們必須思考,必須要為將來可能出現(xiàn)的情況做一些準(zhǔn)備。其實,軟件中了不起的接口的思想,不就是源于此嗎?因此,思考未來,但等到需要時再實現(xiàn)。
    變更案例有助于我們思考未來,變更案例就是你在將來可能要(或可能不要)滿足的,但現(xiàn)在不需要滿足的需求。當(dāng)我們在做架構(gòu)設(shè)計的時候,變更案例也將會成為設(shè)計的考慮因素之一,但它不可能成為進行決策的考慮因素。很多的時候,我們沉迷于設(shè)計通用系統(tǒng)給我們帶來的挑戰(zhàn)之中,其實,我們所做的工作對用戶而言是毫無意義的。
    架構(gòu)的穩(wěn)定
    架構(gòu)簡單化和架構(gòu)的穩(wěn)定性有什么關(guān)系嗎?我們說,架構(gòu)越簡單,其穩(wěn)定性就越好。理由很簡單,1個擁有4個方法和3個屬性的類,和1個擁有20個方法和30屬性的類相比,哪一個更穩(wěn)定?當(dāng)然是前者。而架構(gòu)最終都是要映射到代碼級別上的,因此架構(gòu)的簡單將會帶來架構(gòu)的穩(wěn)定。盡可能的讓你的類小一些,盡可能的讓你的方法短一些,盡可能的讓類之間的關(guān)系少一些。這并不是我的忠告,很多的設(shè)計類的文章都是這么說的。在這個話題上,我們可以進一步的閱讀同類的文章(關(guān)于 refactoring 的思考)。
    辨正的簡單
    因此,對我們來說,簡單的意義就是不要把未來的、或不需要實現(xiàn)的功能加入到目前的軟件中,相應(yīng)的架構(gòu)設(shè)計也不需要考慮這些額外的需求,只要剛好能夠滿足當(dāng)前的需求就好了。這就是簡單的定義??墒窃诂F(xiàn)實之中,總是有這樣或者那樣的原因,使得設(shè)計趨向復(fù)雜。一般來說,如果一個設(shè)計對團隊而言是有價值的,那么,付出一定的成本來研究、驗證、發(fā)展、文檔化這個設(shè)計是有意義的。反之,如果一個設(shè)計沒有很大的價值或是發(fā)展它的成本超過了其能夠提供的價值,那就不需要去考慮這個設(shè)計。
    價值對不同的團隊來說具有不同的含義。有時候可能是時間,有時候可能是用戶價值,有時候可能是為了團隊的設(shè)計積累和代碼重用,有時候是為了獲得經(jīng)驗,有時候是為了研究出可重用的框架(FrameWork)。這些也可以稱為目的,因此,你在設(shè)計架構(gòu)時,請注意先確定好你的目的,對實現(xiàn)目的有幫助的事情才考慮。
    Scott W.Ambler在他的文章中提到一個他親身經(jīng)歷的故事,在軟件開發(fā)的架構(gòu)設(shè)計過程中,花了很多的時間來設(shè)計數(shù)據(jù)庫到業(yè)務(wù)邏輯的映射架構(gòu),雖然這是一件任何開發(fā)人員都樂意專研的事情(因為它很酷)。但他不得不承認,對用戶來說,這種設(shè)計先進的架構(gòu)是沒有太大的意義的,因為用戶并不關(guān)心具體的技術(shù)。當(dāng)看到這個故事的時候,我的觸動很大。一個開發(fā)人員總是熱衷于新奇的技術(shù),但是如果這個新奇技術(shù)的成本由用戶來承擔(dān),是不是合理呢?雖然新技術(shù)的采用能夠為用戶帶來效益,但是沒有人計算過效益背后的成本。就我開發(fā)過的項目而言,這個成本往往是大于效益的。這個問題可能并沒有確定的答案,只能是見仁見智了。