(這一節在第一版的 《構建之法》中沒有, 是《構建之法》電子書(多看版), 和紙版書第二版中新增加的內容,紙版書第二版預計2015年6月出版)
11.1 分析和設計方法
我們寫軟件就是要解決用戶的需求,我們需要表達和傳遞下面這些信息:
在“需求分析”階段,我們要搞清楚
在問題領域中的現實世界里,都有哪些實體,如何抽象出我們真正關心的屬性,實體之間的關系是什么,在這個基礎上,用戶的需求是什么,軟件如何解決用戶的需求。
在“設計與實現階段”,我們要搞清楚
軟件是怎么解決這些需求的?
在“測試”和“發布”階段,我們要搞清楚
軟件真的解決了這些需求了么?
軟件團隊的所有相關人員都需要處理、了解這些信息,如果在處理的過程中有誤解和遺失,就會導致開發過程中的問題,以至最終產品不能滿足用戶的需求,就像 8.3 節提到的“秋千圖”那樣。那么這些信息怎么表達才能更准確、更能有效地交流呢?
我們先看看兩個初中水平的題目:
- 今有雉兔同籠,上有三十五頭,下有九十四足,問雉兔各幾何?
- 程序員果凍覺得寫程序賺錢不多,他想撈外快。於是他參加了王屋村的搬磚大隊,大隊規定搬磚到目的地,沒有破損則給運費每塊磚四分錢,如果有任何破損或丟失則倒扣一毛五分錢。果凍搬到一半的時候覺得還是坐着寫程序好,最后他搬了一千塊磚,共得三十五塊兩毛五分錢。問果凍搬的磚頭沒有破損的有多少塊?
這兩個問題看似不容易,並且毫不相干,但是本書的讀者應該都能毫不費力地用類似的方法來解答。我想最常見的思路是:
這個問題實質上是二元一次方程求解:
雞兔同籠:
x + y = 35; 4*x + 2*y = 94.
果凍搬磚:
x + y = 1000; 4*x – 15*y = 3525
我們還可以用二維坐標系圖示的方法來得出直觀的解法:
雞兔同籠的圖示解法
看典型解題者的解題過程,有下面的步驟:
1) 理解,抽象:理解問題,過濾掉非核心信息,抽象出關鍵信息和它們之間的關系。(雉就是野雞,我沒看見過活的野雞,在這個問題中它等同於家雞,雞長啥樣?一只雞有幾個頭,幾只腳,兔子長啥樣?... 雞頭、兔頭、雞腳、兔腳要滿足一些關系)
2) 找到合適的數學模型:啊,這就是二元一次方程求解。
3) 根據模型和解法,按部就班地解決問題:這要依賴於對數學原理(交換律,等價性…)和基本操作的掌握。
分析和設計有許多方法:
- 以文字為主的文檔, 如Word、PowerPoint 文檔。正如我們在需求分析和場景設計中看到的那樣。
- 用圖形為主構造的模型, 如 Mind Map (思維導圖),ERD, DFD, UML 的各種圖,甚至包括Flow Chart 流程圖
- 用數學語言的描述,如 Vienna Development Method
- 用類自然語言+代碼構造的描述,如 Literate Programming
- 源代碼加注釋也能描述
11.2 圖形建模和分析方法
我們要給事物建造出一個“模型”,描述事物、事物的屬性、事物之間的關系(靜態的)以及各個事物之間的信息傳遞(動態的)。
下面簡要介紹各種方法,詳細的介紹和具體的應用可以參照專門的教科書和文獻。
11.2.1 表達實體和實體之間的關系(Entity Relationship Diagram, Entity Relationship Model,Mind Map)
思維導圖(Mind Map)
“一圖勝千言”, 人們經常用圖形來幫助他們了解概念,強化記憶。思維導圖是其中的一個例子。思維導圖沒有嚴格的語法定義,一般來說是從圖形的正中開始寫下一個概念,然后按照繪圖者所關心的屬性擴展,幾乎每個人都能馬上開始畫圖。這個看似簡單的工具其實很適合團隊一起討論和理解核心概念 – 例如,我們的主要用戶有什么特點、什么需求。
圖 xxxx 思維導圖的例子
思維導圖形式靈活,適用於很多鼓勵探索、發散思維的場合(如頭腦風暴會議),但是它的圖形元素缺乏嚴格的語法和語義。
實體關系圖 (Entity Relationship Diagram)
如果我們着重於表達現實世界中的實體和它們之間的關系,那么實體關系圖ERD是最自然的表達方式。下面是實體關系圖的一個例子,表達了大多數讀者都比較熟悉的 “圖書館借書” 的場景:
在我們分析實體之間的關系時,這就是一個理解和抽象的過程,例如,我們可以通過自然語言的幫助把各種元素歸類到它們在ERD里適當的類型中。 這樣的做法也用在了10.3 節 “功能驅動的設計” 中。 請看下表:
表:xxxx 英語語言中不同的詞性和ERD的元素的分類
英語語言中的不同詞性 |
ER D中的類型 |
普通名詞 (表示一類事物,如銀行、客戶、書籍等) |
實體類型 |
專有名詞 (表示一個特定的人或事物) |
實體 |
及物動詞 (客戶取出存款) |
關系的類型 |
不及物動詞 (利息可以升高或降低) |
屬性的類型 |
形容詞 (這種活期賬戶是無利息的) |
實體的屬性 |
當我們要表示實體之間的靜態關系時,ERD 是一個合適的工具。
用例圖 (Use Case Diagram,UCD )
上一章提到的用例(Use Case)也有圖形化的表示。用例圖主要有下列的元素:
- 參與者(Actor): 表示參與系統運作的外部因素,例如用戶,管理員,外部模塊,設備,來自外部的信號等。 通常是一個簡筆畫的小人。
- 系統:通常用一個方框來表示系統的邊界。有時也可以忽略。
- 用例(Use Case):表示系統和參與者交互的一次場景。它是一組動作的集成,而不是一個單獨的內部元素。
- 信息傳遞線:用帶箭頭的線用來表示參與者和系統通過相互發送信號或消息進行交互的關聯關系。
用例圖的元素簡單,繪圖簡明,它的主要目的是盡快讓團隊成員和利益相關者(特別是對技術不熟悉的)理解系統的需求。
11.2.2 表達數據的流動 (Data Flow Diagram)
當我們要關注數據在不同的實體之間依賴一定的規則流動的時候, DFD 是一個合適的工具。還是用大學圖書館管理系統為例,它有什么數據流過呢?
從下圖來看,流過的數據還真不少。我們簡要地列出幾個例子:
1) 和管理機構相關的數據流
管理機構可以發出指令,“改變讀者借書數量的上限”,這樣的信息會導致圖書館的處理規則發生變化,並且會導致相關信息出現在“公開顯示設備”– 例如網頁,或者電子公告板上。
管理機構可以查詢一定時間內圖書借閱情況的明細或統計信息,這些信息或者返回到管理機構(例如,借書欠款最多的讀者),或者出現在“公開顯示設備”上 (例如,本月熱門人文類書籍前十名)。
2) 和讀者相關的數據流
讀者可以查詢、預定、借出書籍。
3) 和新書入庫相關的數據流
新書入庫的時候,書的各種屬性會被錄入到系統內的“圖書數據庫”,同時內部管理系統能觸發流程,讓預定某書的讀者知道,他關心的書已經到貨。
4) 和時間相關的數據流(圖上沒有表示)
時間也是信息,當某個時間點到達的時候, 系統內部的邏輯會觸發一系列動作,導致信息的處理和流動,例如每天晚上11點開始統計第二天圖書到期的讀者,並給這些讀者推送催還消息。
每一個數據的操作還可以進一步細化,形成一個新的、更低層次的DFD。這些數據流能引導設計者全面設計系統的信息處理流程。DFD 還能幫助系統得到安全設計,設計者可以分析能影響本系統的信息都從哪里來,外部數據和內部數據的邊界在哪里?如果我們盲目相信信息源發出的數據,是否會造成嚴重后果?敏感數據都流到哪里去了? 如果數據的目的地沒有合適的保護,是否會造成敏感數據的泄露[XZ1] ?等等。
11.2.3 表達控制流 (Flow Chart, Finite State Machine)
我們在計算機理論基礎課上都學過有限狀態自動機(Finite State Machine, FSM), 在程序設計語言基礎課上都學過基本的流程圖,這里不再贅述。
11.2.4 統一的表達方式(Unified Modeling Language, UML)
這些圖形建模方法各有特點,它們使用了不同的幾何圖形、標注規則、專有詞匯和顏色。人們自然會想,能否有一個統一的表達方式? UML 就是這樣的回答。 UML在20世紀90年代伴隨面向對象的方法發展,在工業界的應用和反饋中成熟起來,2004年發布的UML2.0是一個相對穩定的版本。
表xxxx 各種圖示建模方法的大致特點
各種分析建模方法 |
從結構化數據的角度看 |
從面向對象的角度看 |
從控制的角度看 |
強調靜態 |
ERD |
Class Diagram |
|
強調動態,交互 |
DFD,UCD,Activity Diagram |
Sequence Diagram |
FSM,Flow Chart, UML State Machine |
|
|
|
|
對於這些圖形化的輔助工具的價值,不同的人有不同的看法。
在《Coders at Work》這本書里面, Java 架構師,暢銷書 《Effective Java》的作者Joshua Bloch 對於“你用過UML 設計工具么?” 的回答是:
“沒有。 能把設計畫成圖,讓別人理解當然很好。 但是說實話我真的記不起來哪些模塊應該是圓形,哪些是方形。”
谷歌研究院的院長 Peter Norvig被問及同樣問題的時候說:
“我從來不喜歡UML類型的工具,如果你不能通過計算機語言表達 (UML 要表達的東西), 那就是這種語言的弱點。“
像任何新技術一樣,以UML 為代表的圖形化分析方法的確解決了不少實際問題,但是也引發了一些誤解、誤用、狂熱和“銀彈”的信仰。UML 的設計者和推動者之一Grady Booch 說到:
在UML 出現之前和之后,軟件項目成功的關鍵依然是 – 智慧地使用技術、遵從一個好的軟件開發過程、有經驗的開發者和適當的技能組合[XZ2] 。
11.3 其他設計方法
在計算機軟件發展的過程中,科學家和工程師們還嘗試了很多其他方法, 它們在不同程度上解決了一些局部問題,從不同的方面推動了相關領域的發展。
形式化的方法 (Formal Method)
很多軟件需求(例如計算機語言的編譯器)可以抽象為對符號的運算和變換,很多軟件的某些核心功能需要嚴密地驗證,保證沒有問題。一些科學家一直在努力,希望用無歧義的、形式化的語言描述我們要解決的問題,然后用嚴密的數學推理和變換一步一步把軟件實現出來,或者證明我們的實現的確完整和正確地解決了問題。在這個領域一個比較成熟和經過實踐考驗的方法是 Vienna Development Method (VDM)。
文學化編程 (Literate Programming)
程序員在寫程序的時候,要理解在文檔中的需求,同時還要在程序里寫相關的注釋,這些不同目的的“寫作”各有價值,但是一旦需求或程序發生變化,這些不同的文檔很難保持同步。 更不用說程序員最常見的毛病 “我以后會加上注釋的…”Donald Knuth 在20世紀70年代末開始嘗試並提倡 Literate Programming 的思想並在自己的軟件項目中身體力行。這一方法和常見的 “寫程序,時不時加上一些注釋” 相反, 它是 “寫文檔,時不時有些代碼”。 它使用了宏(Macro)來進行抽象和信息隱藏。 通過工具的支持,它的源代碼可以提取出讓計算機編譯執行的部分(叫 Tangle),以及文檔(叫 Weave)。
有興趣的同學還可以探索更多的設計方法。
把這些不同方法列舉在這里,一方面是要說明,有多種設計軟件的方式,它們在不同的歷史階段,在各自適合的范圍內能有效地發揮作用。 如果我們能建立一個正確的抽象模型,這個模型可以適合很多不同的具體應用, 例如一個基於關系數據庫增刪改查的抽象模型可以適用於圖書館管理系統,學籍管理系統,簡單的物流倉庫管理系統等。 另一方面,軟件團隊可以寫很多文字,畫很多圖,寫很多公式,還可以口若懸河地互相傾訴,但是最后在電腦上運行的,是代碼。
回到前面提到的“雞兔同籠”問題,人們還想出了另一解法:
假設籠子里所有的兔子都坐在地上,舉起它們的前腿,這樣,三十五個動物都有兩只腿和地面接觸。那么,這個籠子里原來的“下有九十四足”就變成了“下有七十足”。兔子一共舉起了二十四只前腿,每只兔子都有兩只前腿,那么籠子里就有十二只兔子,那么三十五只動物的另外二十三只就是雞。
我們怎么用數學公式或圖形來表達這一方法呢?這一方法如何能推廣到“果凍搬磚”的問題中呢?
[XZ1]加入注釋: 參見: Coders at Work: Reflections on the Craft of Programming. 作者:Peter Seibel, ISBN 978-1430219484
[XZ2]注解:參見 文章 Death by UML Fever
作者 ALEX E. BELL, 和Grady Booch 在文章后的評論。 http://queue.acm.org/detail.cfm?id=984495
[XZ1]新注解:請參考網上關於安全設計,威脅模式分析(Threat Modeling)的文章,例如: https://msdn.microsoft.com/en-us/magazine/cc163519.aspx