軟件設計是怎樣煉成的(7)——細節決定成敗(詳細設計)


摘要:

當我們需要考慮類、類的內部細節、類之間的關系時,這時我們已經開始做詳細設計了。詳細設計不一定是一份文檔,也不一定是Word文檔,詳細設計也不一定叫“詳細設計”,有時候“編碼就是設計”也是未嘗不可的。對於MIS類型系統來說,架構設計和數據庫設計做好的前提下,詳細設計的難度其實是比較小的了,但MIS系統會有一些特殊的需求點,我們需要識別出來並想清楚應對辦法。如果你做的軟件是高技術含量的非MIS系統,情況將會更加復雜。

 

大綱:

1.什么是優秀的設計?
2.優秀的設計能節省項目工作量
3.優秀設計從分析需求開始
4.軟件系統不是木桶型的
5.軟件設計的“大道理”
6.規划系統骨架——架構設計
7.打造系統的底蘊——數據庫設計
8.細節決定成敗——詳細設計
9.用戶感覺好才是真的好——用戶體驗設計
10.持續提升設計水平

 

本文章是系列文章之一,如果你還沒有看過之前的文章,建議先看完前面的文章再看本篇,這樣效果更好。

 

8.細節決定成敗——詳細設計

 
 
8.1 什么是詳細設計?
 
當我們需要考慮類、類的內部細節、類之間的關系時,這時我們已經開始做詳細設計了。詳細設計不一定是一份文檔,也不一定是Word文檔,詳細設計也不一定叫“詳細設計”,有時候“編碼就是設計”也是未嘗不可的。下面是我的一些最佳實踐:
 
實踐一:模塊設計
我早期的一些項目會寫一份詳細設計文檔,但后來的項目我會將詳細設計文檔分拆為N份模塊設計文檔了,這樣做的兩大好處是:
1)一份詳細設計文檔太大,不利於閱讀,不利於指導編碼工作,分拆后就好多了;
2)N個模塊設計的任務可以分派給不同的軟件設計師(或程序員)來負責。
 
實踐二:代碼就是設計
有時候我會“偷懶”,我覺得沒有必要再寫什么設計文檔,直接在開發工具中定義好類,寫好類的公開接口,寫好注釋等等,這時我其實就是在做詳細設計的工作,我將代碼框架寫好后,才寫具體的實現代碼。這種工作模式其實就是將詳細設計與編碼實現融合在一起了,效果和效率更好!當然不是說不再需要寫Word文檔格式的詳細設計了,對應比較復雜的詳細設計,一般還是需要通過另外的文檔來描述一下比較好。
但可能會有一種比較詳見的“特殊”情況:你可能會遇到開發人員死活寫不出Word文檔格式的詳細設計,你和他溝通多次后,他還是寫不出有質量的Word文檔格式的詳細設計,這時你不如讓他直接寫代碼,先寫個框架看看,然后你通過評審代碼來修正他的設計。
 
實踐三:Demo就是設計
設計邏輯復雜時,可能需要文檔來應對,但文檔畢竟是紙上談兵,可能最切實的辦法是做一個 Demo 實現你的算法和設計思路。只要 Demo 是 Work 的,就可以將這個 Demo 的代碼重用到實際的項目中。
舉一個例子:曾經某項目中需要寫代碼解決判斷一個點是否在多邊形內,算法有點麻煩,光寫文檔說算法沒有實質的價值,於是我用了半天時間寫了實現的代碼和測試的代碼,將這個Demo提交給項目組。
 
實踐四:“無”詳細設計
無詳細設計的意思不是真的不考慮詳細設計了,而是對於這種情況我們已經駕輕就熟了,幾乎是閉着眼睛都會做了,所以我們就“無”詳細設計直接編碼了。
 
 
8.2 詳細設計的基礎
 
我曾經評審過一份設計文檔,該文檔內容詳細、思路清晰,清楚描述了某些技術環節的實現辦法,而且實現辦法都是可行和有效的,這個文檔可以算是一份比較好的詳細設計文檔了。但可惜的是,整個項目只有這樣的一份設計文檔,從全局來看這個文檔只解決了局部問題,缺失了一些核心內容:
1)沒有架構設計的的內容;
2)沒有數據庫設計的內容;
3)系統的需求很復雜,大部分的需求沒有對應的設計考慮。
 
前面的文章曾提到,我做過的項目一般至少會有一份概要設計文檔,詳細設計文檔不一定是必須的。詳細設計固然重要,但針對整個系統的全面考慮更加重要,詳細設計之前應該具備以下條件:
1)應針對全部需求(包括功能性和非功能性的需求),系統需要有整體上的考慮,也就是前文提到的架構設計。
詳細設計需要考慮類、類的內部細節、類之間接口等,這些是需要符合系統的總體架構和分層架構的。
2)應有數據庫設計。
如果沒有數據庫設計,建築在數據庫之上的代碼是很難寫的。當然如果你是用“由中間到上下”的設計方法的話(什么是“由中間到上下”?請參考前面的文章),數據庫設計沒有,只要有中間層的建模的話,表現層和邏輯層的代碼還是可以寫的,但數據庫操作層的代碼還是依賴於數據庫設計的。
3)部分情況下,還應該有部分或全部的用戶體驗設計(用戶體驗設計下一篇會分享)。
用戶體驗設計主要考慮的是軟件的表現層,最能充分體驗“由頂而下”的設計思路,將會直接影響具體的代碼實現。
 
一般情況下我們應該在架構設計和數據庫設計的基礎上進行詳細設計,否則很可能會讓我們僅僅關注了局部的問題,而沒有抓住其他更加重要的問題和全局的問題。如果沒有架構設計和數據庫設計,直接詳細設計是不是一定不可行呢?有以下的一些特殊情況(不限於此噢):
1)如果果你的情況是在原有系統上升級改造,系統原有的架構和數據庫設計基本不變,那么直接進行詳細設計是合適的做法;
2)有時候有些局部問題雖然很“局部”但又相當特殊或重要,哪怕沒有來得及完成架構設計和數據庫設計,也可以先進行詳細設計的。
后文我們先從正常思路介紹詳細設計,也就是先有架構設計和數據庫設計再有詳細設計,然后再分享一些上面第2)點的情況。
 
 
8.3 詳細設計是架構設計的延續
 
前文的架構設計提到我們要對系統進行兩個層次的拆解,分別是:
第一層拆解:思考系統需要開發什么軟件和數據庫等;
第二層拆解:考慮組件(Component)、代碼包、某個分層等等,可能是“物理分拆”也可能是“邏輯分拆”。
不太記得或者看不懂的朋友,請先看看前面的文章啦。
而詳細設計其實就是:
第三層拆解:進一步細化出類、類對外接口、類的內部細節等。
 
通常我會用UML的順序圖(Sequence Diagram)來表達“第三層拆解”,請看一個簡單一點的圖,了解一下順序圖。
 
圖8.1 詳細設計-順序圖1
 
我們通過這個圖了解兩個事情:
1)順序圖的基本語法;
2)順序圖如何表達詳細設計。
 
這個圖表示的是用戶在某個查詢頁面輸入查詢內容、點擊查詢按鈕等這些用戶交互及背后的程序設計。通常順序圖最左邊畫的是用戶,僅次之是軟件的表現層的某個頁面(界面),用戶與表現層的之間的交互,會導致表現層后面的類的一系列動作。這個圖還算比較簡單,請看下面這個我在N年前完成的某項目中的其中一個順序圖:
 
圖8.2 詳細設計-順序圖2
 
架構設計需要考慮全部需求后設計出來,也就是說”全部需“求驅動架構設計,當然某些特殊的需求點需要特別關照;數據庫設計主要是業務概念模型驅動的,業務建模及進一步提煉可以幫助我們設計出更有彈性的設計。那詳細設計是不是仍然需要”需求驅動“呢?這是必須的!
我們可能用用例(UseCase)、用戶故事(User Story)或者是功能點等方法表示需求,不管怎樣的表示辦法,最終都會拆解為一條條比較細的需求。每一條需求具體如何實現呢?順序圖就是表達這個實現方法的好工具!圖8.1 和 圖8.2 分別說明的都是某個需求點的實現方法,圖8.1 是查詢用例的詳細設計, 圖8.2 是修改材料設備信息的詳細設計。一個系統的詳細設計,就可以用類似圖8.1和圖8.2的圖逐一表示出來。
 
我們通過下圖再充分理解一下需求如何驅動詳細設計:
圖8.3 詳細設計-順序圖3
 
上圖紅色框框部分的內容是需求,用戶和系統界面之間的交互設計是對需求的進一步細化;藍色框框部分是在需求驅動下的程序實現,此圖實現部分比較簡單,大部分的程序實現邏輯都會比上圖復雜,會涉及到邏輯層、數據操作層還有一些共用模塊之間的調用等等。詳細設計除了要需求驅動,同時也要需要符合架構設計,代碼也需要基於已有的數據庫設計,換句話說就是詳細設計是需求、架構設計及數據庫設計三者同時驅動的。
 
本小節所列舉的詳細設計的例子都是MIS類型系統的例子,基本上圍繞數據庫的增刪改查進行,設計難度其實並不是很大。前面已經提到,如果對於已經很熟悉的情況,你沒有必要再用順序圖來畫一次了,直接可以”無”詳細設計;但如果你的團隊成員還不是很熟悉數據庫的增刪改查,或者實現邏輯比較復雜,這樣就很有必要進行詳細設計了。
 
本小節的例子比較“常規”,老鳥可能覺得沒啥難度,下小節的難度將會增加。
 
 
8.4  詳細設計是解決局部問題的良方
 
前面提到,有些局部問題雖然很“局部”但又相當特殊或重要,哪怕沒有來得及完成架構設計和數據庫設計,也是需要先進行詳細設計的。
舉三個例子:
 
案例1:針對網絡負載平衡的特殊考慮
某客戶的Web服務器采用網絡負載平衡,有兩台Web服務器,這與我們慣常的一台Web服務器場景很不一樣。我們打算使用公司的框架來開發這個系統,但這個框架的其中一個地方很可能會出問題。框架使用了靜態變量用來記錄數據庫中ID的最大值,當增加一條記錄時就 ID_Max = ID_Max + 1,將新的 ID_Max 作為新增加記錄的ID。這樣在兩台Web服務的場景下,就會有兩個靜態的 ID_Max,兩邊都很可能會出現不准確的情況,導致數據插入到數據庫中時出錯。本身這個修改並不算復雜,但我們需要同時考慮兼容框架,因為這個框架是同時支持 SQLServer 和 Oracle 數據庫的,我們的首席設計師很厲害,在框架層面解決了這個問題,不僅可以繼續保持框架的兼容性,還擴大了框架的適應面。
 
案例2:點是否在多邊形內的求解
這個幾何問題是由業務問題轉化而已來的,這是一個某移動通訊公司的系統,先簡單介紹一下業務。
我們的手機是通過基站進行通訊的,如果附近沒有基站,就會出現手機沒有信號的情況。我們所居住的城市當中,一般會有上百成千的基站,保證我們通訊暢通。基站與基站之間形成的通訊網絡,會划分為以基站為中心的多個“多邊形”,形成一個好像蜂窩的樣子,這就是我們經常聽說的蜂窩網絡。但有可能會出現某個地方打電話有問題的情況,這個出問題的地“點”位於哪個“多邊形”呢,系統需要通過點的坐標找到這個點在哪個多邊形范圍內,進一步定位到是哪個基站可能出問題。 
上述就是原始的業務背景,我們將這個需求演化為一個幾何問題,當時我參考了“放射線”的算法,直接通過“Demo法”來完成這個設計。前文的”實踐3:Demo就是設計“中提到的例子,就是這個例子了!
 
案例3:讓程序支持 Undo 和 Redo
如果要求你的系統支持 Undo 和 Redo,不知道你會如何考慮呢?支持 Undo 和 Redo 是非常酷的,但難度是相當之高的,你會先完成架構設計和數據庫設計才考慮嗎?23設計模式中的命令模式可以幫助我們,但命令模式僅僅是給出了解決問題的框架而已,你還需要演化為更實際的內容,否則又犯了”放之四海而皆准“的毛病了。命令模式很有難度,但很有意思和很有用,我們這里不詳解命令模式,大家可以參考我的設計模式方面的文章。
這里舉這個例子是想說明:某些需求看上去好像是僅僅很小的一個點的要求,有可能影響面很大,你可能需要先針對這個點去思考詳細的實現辦法,然后才能幫助你想清楚架構設計及數據庫設計。
 
小結一下詳細設計解決局部問題的兩種特殊情況:框架未確定之前的技術預研,案例1、3屬於這個情況;框架確定與否都不影響的局部問題求解,案例2屬於這個情況。一般認為詳細設計是概要設計之后的,大部分情況確實如此,但通常我們進行概要設計之前還很可能需要針對某些需求點進行技術預研,這些技術預研是需要用詳細設計的程度來進行。
 
需要補充說明的是:某些技術難點的設計,通常僅僅靠順序圖是搞不定的,甚至不能用順序圖,我還會用到類圖、對象圖、活動圖和狀態機圖等等,類圖用到的機會最大,你看看23設計模式,設計思路基本上都是用類圖來表達的。UML圖僅僅是一種工具,前文提到的“實踐二:代碼就是設計”和“實踐三:Demo就是設計”,對於難度高的詳細設計是經常需要用到這兩招的。
 
 
8.5 需要從詳細設計中提煉出需要全局考慮的內容
 
前文提到”詳細設計是架構設計的延續”,其實還需要補充的是“詳細設計需要持續完善架構設計”。詳細設計過程中,我們會發現很多共性的內容,需要提煉為整個程序需要遵循的設計規范。下面是一些例子:
1)用戶體驗設計;(下一篇再詳細介紹)
2)輸入合法性判定;
3)批量數據的傳輸約定;
4)實體類的生命周期;
5)邏輯類的生命周期;
6)並發沖突的處理原則,包括判定辦法、提示辦法;
7)連接打開、關閉原則;
8)采用事務的原則;
9)異常處理機制;
10)日志記錄機制;
……
 
 
8.6 詳細設計小結
 
對於MIS類型系統來說,架構設計和數據庫設計做好的前提下,詳細設計的難度其實是比較小的了,你可以用順序圖來完成設計,注意做到需求驅動詳細設計,注意要滿足架構設計和數據庫設計。不過不少MIS會有一些特殊的需求點,我們需要識別出來並想清楚應對辦法,某些特殊的需求點還需要進行前期的技術預研。
如果你做的不是MIS類型的系統,而是設計難度很高的軟件,比方說技術含量很高的CAD軟件、人工智能很牛逼的某些家用游戲,要解決這些軟件的詳細設計,你需要設計模式、工程繪圖、數學、物理、人工智能等很多知識支撐才能搞得掂了!
 
 

本文是系列文章的其中一篇,要做軟件設計師一點都不簡單啊,請留意后續文章!

 

如果本文對你有幫助,麻煩點一下“推薦”啦,謝謝!

 

 

作者:張傳波

創新工場創業課堂(敏捷課程)講師

軟件研發管理資深顧問

CMMI首席專家

《火球——UML大戰需求分析》作者

軟件知識原創基地創辦人


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM