本文謝絕無授權(quán)轉(zhuǎn)載。
在目前大部分的軟件開發(fā)組織中殉摔,敏捷開發(fā)已經(jīng)成為毋庸置疑的標配凄贩。隨著數(shù)位技術(shù)大神和布道師的宣揚和數(shù)量龐大的敏捷教練的身體力行式推廣淑玫,商業(yè)環(huán)境和客戶需求變更速度的日益加快崎岂,采用端到端交付周期更短的敏捷開發(fā)過程基本已經(jīng)成為項目成功的必要條件捆毫。
軟件設(shè)計的剛需被敏捷了嗎?
工作流程的變更以及開發(fā)節(jié)奏的加快并不能繞開一個很核心的問題:寫出容易維護方便擴展的代碼的復(fù)雜程度本質(zhì)上沒有改變该镣;軟件的維護周期越長冻璃,迭代的版本越多,這個基本問題就越突出损合。要想順利解決這一問題省艳,只能依賴于系統(tǒng)具有相對良好的設(shè)計,使得添加新的功能不會輕易破壞原有的結(jié)構(gòu)嫁审,出現(xiàn)問題的時候跋炕,不需要大范圍地對系統(tǒng)做出變更。
傳統(tǒng)的瀑布式方法希望通過借鑒成熟的建筑行業(yè)的做法律适,采用預(yù)先大規(guī)模的架構(gòu)設(shè)計辐烂,對系統(tǒng)做好明確的分割;繼而進行不同層次的設(shè)計捂贿,直到所有可以預(yù)見到的需求都得以滿足纠修,然后才開始進行的代碼的編寫和構(gòu)建。這種方法生產(chǎn)出來的軟件交付工期很長厂僧,適應(yīng)性很差扣草,除了少數(shù)特殊行業(yè)之外基本已經(jīng)被市場所淘汰。
敏捷宣言提出了一些基本的原則來指導(dǎo)我們怎樣用相對更"敏捷"的方式開發(fā)和交付我們的軟件颜屠;通過多個不同的迭代辰妙,增量式的構(gòu)建和持續(xù)交付系統(tǒng)來降低風(fēng)險。然而軟件本身的復(fù)雜性導(dǎo)致我們不能將客戶的需求一對一的翻譯成代碼甫窟,像搭積木一樣構(gòu)建出來一個可以輕易維護的系統(tǒng)密浑。因為新加入的需求很可能導(dǎo)致原有的代碼結(jié)構(gòu)無法適應(yīng)新的需求;某些為了盡快完成需求而做出的關(guān)鍵的假設(shè)可能必須被打破導(dǎo)致添加新的需求會破環(huán)大量已有的功能粗井。如何做出恰如其分的軟件設(shè)計尔破,既能滿足現(xiàn)有的短期需求,又能平衡潛在的變更浇衬。
各種不同的敏捷實踐方法論對如何管理用戶的需求懒构,如何增強不同角色的溝通,如何實施日常的開發(fā)和測試活動径玖,如何驗證需求保證已經(jīng)交付的承諾不被變更所破壞,如何規(guī)劃和平衡資源和進度等復(fù)雜問題都給出了豐富的可選實踐供項目管理人員裁剪颤介;對于如何做軟件設(shè)計以及做多少軟件設(shè)計并沒有很詳盡的描述梳星。就連基本的是否需要軟件設(shè)計赞赖,以及需要多少軟件設(shè)計,怎樣算作過度設(shè)計都語焉不詳冤灾。
真的需要軟件設(shè)計嗎前域?
大部分情況下,對于這個基本的問題我想答案應(yīng)該是肯定的韵吨,除非你是在做一個很小的個人項目匿垄。如果需要牽扯到多個人一塊合作并且最終的產(chǎn)品需要維護比較長時間,那么起碼某種程度的軟件設(shè)計應(yīng)該是不可或缺的归粉。畢竟軟件開發(fā)活動本身也是圍繞著人展開的椿疗,既然需要多個不同知識背景,不同技能糠悼,不同角色的人一起來協(xié)作交付功能復(fù)雜多變的軟件届榄,那么必然需要一些設(shè)計保證參與其中的人有一致的理解。
敏捷運動的早期一個常見的誤解就是倔喂,敏捷軟件開發(fā)不需要軟件設(shè)計铝条,不需要軟件架構(gòu),只需要采用極限編程席噩,將人們聚集一個公共空間里班缰,直接動手寫代碼就行了;復(fù)雜的設(shè)計文檔都是浪費悼枢,都是可以避免掉的埠忘,代碼就是最好的設(shè)計文檔。這樣的過程可能適合于幾個能力超強的程序員聚在一起做的臨時小項目萧芙,放到更廣泛的商業(yè)環(huán)境則難以持續(xù)下去给梅。人員的流動,特殊需求的變更双揪,性能問題的修補會使一個一開始看起來極其簡單的幾個源代碼文件組成的小項目演進成難以維護的“龐然大物”动羽。
如果系統(tǒng)沒有明確的分工和邊界,沒有相對清晰的職責(zé)分工和交互限制渔期,軟件的結(jié)構(gòu)很容易陷入“大泥球”結(jié)構(gòu)而不可維護运吓,試想如果代碼里的每一個包或者類都有可能和另外其它的任意一個類有交互關(guān)系,即使是一個絕對代碼行數(shù)很小的項目也會變得無法繼續(xù)添加新的功能疯趟。
哪些東西應(yīng)該包含在軟件設(shè)計中拘哨?
所謂的設(shè)計其實可以理解為關(guān)于如何組織軟件各個部分(特性和行為分割)的一些決策,以及做出相應(yīng)決策的一些原因信峻。
敏捷場景下倦青,重構(gòu)的重要性以及早已經(jīng)深入人心,因而容易經(jīng)由重構(gòu)來去掉“壞味”的部分就不宜放在設(shè)計中盹舞。因為一般為了重用的目的都會將設(shè)計決策寫下來供后續(xù)使用产镐;如此一來必然產(chǎn)生一些維護成本隘庄;而維護設(shè)計文檔的開銷一般比代碼要大很多。因此容易通過重構(gòu)而優(yōu)化的部分癣亚,放在專門的軟件設(shè)計中顯得有些得不償失了丑掺。畢竟敏捷軟件開發(fā)的基本思路就是消除浪費,使得投入產(chǎn)出比最大化述雾。
某些跟具體實現(xiàn)技術(shù)相關(guān)而和核心業(yè)務(wù)需求關(guān)系比較遠的決策街州,大部分也不適宜包含在軟件設(shè)計中。譬如期望某部分關(guān)鍵數(shù)據(jù)需要做持久化以保證系統(tǒng)異常重啟的時候依然可以恢復(fù)玻孟。對于業(yè)務(wù)需求而言唆缴,這塊數(shù)據(jù)需要持久化是重要的,但是如何做持久化取募,又可能是易變的琐谤,譬如今天是考慮用文件來做持久化就可以了,將來可能發(fā)現(xiàn)不夠必須用關(guān)系數(shù)據(jù)庫玩敏,或者甚至關(guān)系數(shù)據(jù)庫可能也不是一個合適的選擇斗忌,得要用鍵值對數(shù)據(jù)庫。識別到可能變化的部分旺聚,并將不變的部分抽象出來织阳,放入設(shè)計中可能就足夠了。這樣技能照顧到當(dāng)前的需求砰粹,又能滿足將來擴展的需要唧躲。至于具體是怎樣實現(xiàn)的,看代碼就足夠了碱璃。
需求的概念抽象化弄痹,和軟件的靜態(tài)模型可以作為設(shè)計的中心之一,必須詳細考慮并歸檔維護嵌器。之所以要對需求進行抽象化處理肛真,是因為用戶的期望可能是模糊不清的,甚至是“朝令夕改”的爽航。敏捷方法強調(diào)持續(xù)交付就是為了使用戶早期得到反饋蚓让,及時修正他們的需求,更好的管理客戶的期望讥珍,避免開發(fā)出不符合客戶真正預(yù)期的產(chǎn)品历极,浪費開發(fā)資源不說也浪費了客戶的投資。軟件的靜態(tài)模型是關(guān)于大的軟件職責(zé)的拆分和交互邊界衷佃,這一部分不僅是當(dāng)前進一步開發(fā)的基本依據(jù)趟卸,日后萬一需要重構(gòu)也是很重要的參考,值得花力氣仔細討論達成一致,減少日后維護成本锄列。
軟件的部署和核心模塊的交互在有這方面的變更的時候(新加入模塊或者服務(wù)等)也需要仔細考慮并作為軟件設(shè)計的關(guān)鍵活動新蟆。模塊的邊界是粗粒度的系統(tǒng)耦合的地方,一些關(guān)鍵的交互流程也適宜詳細討論并放在軟件設(shè)計文檔中右蕊。
系統(tǒng)核心的模塊/類以及之間的交互,如果有發(fā)生變更吮螺,也需要第一時間考慮清楚并放置在設(shè)計過程中產(chǎn)生合適的產(chǎn)出饶囚,便于溝通和交流。如果模塊的粒度足夠大(譬如估計有很多的代碼)鸠补,那么哪些部分是對外交互的接口也應(yīng)該提早考慮清楚萝风,并提取出來以便后續(xù)寫代碼以及代碼評審的時候?qū)φ眨_保設(shè)計被正確遵守紫岩。
什么時候應(yīng)該停止繼續(xù)設(shè)計规惰?
敏捷語義下,任何的浪費都是可恥的泉蝌,代價巨大的設(shè)計工作自然也不例外歇万。知道何時還需要仔細討論搞清楚,何時應(yīng)該停止變得尤其困難勋陪,甚至需要一些接近于藝術(shù)化的方法贪磺,需要經(jīng)過大量的實踐經(jīng)驗累積和反思才能做到不偏不倚。
如果發(fā)現(xiàn)所討論的問題可能代碼實現(xiàn)很容易就能完成诅愚,如果考慮不完備寒锚,那么修改代碼的代價非常小,那么就可以立即停止了违孝;因為設(shè)計的目的是為了更好的寫代碼刹前,更好的維護既有的代碼。因此只有重構(gòu)代碼的代價遠遠大于預(yù)先仔細設(shè)計付出的代價是雌桑,才應(yīng)該花費力氣去做這些燒腦的工作喇喉。發(fā)現(xiàn)代碼實現(xiàn)已經(jīng)很容易沒什么問題的時候,就放手去寫代碼或者重構(gòu)代碼吧筹燕。這種情況往往發(fā)生在我們想去“設(shè)計”一些內(nèi)部實現(xiàn)細節(jié)轧飞,而這些細節(jié)對模塊的邊界以及待修改模塊和核心部分耦合很小的情況。
如果是在構(gòu)建一個新的模塊撒踪,而這個模塊和已有的系統(tǒng)有形形色色的復(fù)雜聯(lián)系(耦合)过咬,那么如果這個模塊和已有的系統(tǒng)的各個部分的交互已經(jīng)比較清楚,而且其內(nèi)部實現(xiàn)估計工作量也很小的時候制妄,那么就可以放心將剩下的工作交給聰明的程序員去繼續(xù)了掸绞。將一些細微的工作也放入設(shè)計中,只會使設(shè)計文檔變得龐大而又難以維護。畢竟可工作的代碼比完美的文檔更重要衔掸,雖然后者也很有價值烫幕。
如果對于是否應(yīng)該繼續(xù)設(shè)計有分歧,可以和其它準備實現(xiàn)的程序員坐在一起討論(其實任何時候都應(yīng)該如此敞映,如果團隊規(guī)模比較小而且時間允許)吃挑,看將要寫代碼的程序員是否覺得足夠清楚,返工的風(fēng)險是否足夠得小假消。如果對于一些核心的模塊或者類的職責(zé)還有不同的認識红选,或者程序員不知道某些改動是應(yīng)該新創(chuàng)建一個子包,還是應(yīng)該在已有的摸個包中修改來實現(xiàn)冕末,那么很可能有些關(guān)鍵的部分沒有設(shè)計清楚萍歉。
決定何時應(yīng)該適可而止也和你的程序員團隊的實際水平和能力密切相關(guān)。一群天才程序員可能需要極少的設(shè)計來達成基本的共識就可以產(chǎn)出高質(zhì)量易維護的代碼档桃,而水平平庸的程序員團隊則需要更多設(shè)計上的預(yù)先討論溝通以達成基本共識枪孩,減少返工。
工具
軟件工程的一個關(guān)鍵要素就是工具藻肄。軟件設(shè)計自然也離不開合適的工具蔑舞,尤其是軟件設(shè)計又是對需求進行抽象的結(jié)晶;選擇合適的工具可以增進協(xié)作和溝通嘹屯,使得設(shè)計輸出是有實際指導(dǎo)作用的而不僅僅是純粹的文檔工作斗幼。
輕量級的文檔工具往往使維護和修改變得更加容易,因為設(shè)計輸出本身也是一個迭代的過程抚垄;便于多人評審和協(xié)作顯得尤其重要蜕窿。目前主流的方式基本都是基于Markdown和Plantuml的;前者可以用來存放文本呆馁,后者則可以用文本的格式來描述UML圖桐经。