1. 程式人生 > >談談領域模型的那些事兒 之 從領域獲取知識

談談領域模型的那些事兒 之 從領域獲取知識

前言:你寫過用例模型嗎?也許有;你寫過領域模型嗎?也許還沒有。在這裡,我們可以嘗試寫寫領域模型,看看它的作用、帶給我們的好處。

隨著RUP在中國的傳播,人們開始嘗試用RUP統一過程來指導軟體的設計和開發,但這些嘗試並不成功。比較普遍的,大家都開始使用用例模型來進行需求階段的分析和設計了。當然,能做出第一步已經非常不錯了,但這遠遠不夠。要做好需求分析,用例模型可以幫助我們分析清楚軟體需求中要求的各個流程,但我們還缺少OO分析。過去,一旦需求分析完成以後,經過簡短的分析過程,馬上就開始進入開發階段。在開發階段,應當設計哪些類,它們的職責是什麼,應當為它們設計哪些屬性和方法,如何協作工作,在何時由誰來建立,以上這些問題都沒有經過系統的分析,而是非常隨意地新增類,非常隨意地新增屬性和方法,並且非常隨意地在某個時刻建立物件。這樣的設計,雖然可以實現需求所要求的功能,但它必然不能低耦合、高內聚,實現一個靈活多變、可維護性高的系統。即使有個別有經驗的程式設計師的靈光閃現,但那隻能在某個區域性得到了優化,從整體上依然不是一個理想的分析設計。總之,沒有經過系統的OO分析和設計,我們不能提高我們的程式碼質量。要進行系統的OO分析和設計,在需求分析階段就要從領域模型開始。

從嚴格意義上講,領域模型還不算是真正的OO分析和設計。真正的OO分析和設計是在需求分析人員完成需求分析以後,由技術人員完成的分析和設計(即分析模型和設計模型)。但領域模型在為日後的分析與設計準備著素材。用例模型為日後的分析與設計準備著流程操作方面的素材(動態模型),而領域模型為日後的分析與設計準備著類與類之間關係方面的素材(靜態模型)。領域模型就是描述日後可能關心所有事物(可能描述成類,也可能描述成類中的屬性)以及它們之間的關係。因此,領域模型是一批類圖,以及它們的說明。它與用例模型在需求分析階段一起進行編寫,不分先後順序,並在完成該階段以後作為工作成果一起提交。

和用例模型一樣,領域模型的建立也是以迭代的方式逐漸推進的。但一直以來,對領域模型設計進行描述的書籍比較少,即使在大師Craig Larman的經典著作《UML和模型應用》中對領域模型的也比較模糊。然而,這一現狀被另一位大師Eric Evans打破,在他的經典著作《領域驅動設計》中,詳細為我們描述瞭如何通過領域模型,驅動我們進行OO分析和設計。下面我們開始我們的領域驅動之旅吧。

從業務領域提取知識



在一個陽光明媚的下午,我們一個個西裝革履、精神抖擻地來到了客戶的辦公現場。在一個明亮的會議室裡,寬大深褐色的橢圓木桌旁已經聚集了十來個業務人員。看到我們進來,大家握手問候。相繼就座後,互相介紹,往來寒暄,嘮嘮家常。共同的家鄉,或熟或不怎麼熟的某個人,都可能成為拉近彼此關係的理由。逐漸,一切開始進入正題。客戶開始絮絮叨叨的描述自己的需求,而我們則在緊張的做著記錄,是不是問一些問題,表明我們的立場,抒發我們的建議。在這樣一個過程中,客戶會描述他們的每一個業務,會講解每個業務的流程,他們會講出一些業務領域的專業詞彙(儘管有些你當時還不太懂)。在這樣一個過程中,作為需求分析員,你應當非常注意業務流程中的一些關鍵詞彙,你應當(在當時或者過後)將它們提取出來,通過詢問客戶,弄清楚他們的定義,以及相互之間的關係。而這些詞彙就是建立領域模型的開始。

這是一個財務軟體的業務討論會,一個業務人員正在跟我講付款單是怎樣製作成憑證的。“每張付款單都有一個商品明細,每個商品明細都有它的價格、數量和金額。”他指著一張付款單向我解釋著。從這句話,我可以提出一些關鍵資訊:付款單、商品明細、價格、數量和金額。付款單與商品明細是一對多關係,並且商品明細聚合在付款單中。每個商品明細都有價格、數量和金額,也就是說,價格、數量和金額是商品明細的屬性,這都很清楚。緊接著,他下面的講解就不是那麼清楚容易了。“如果按照一張單據生成一張憑證,那麼每張付款單生成一張憑證。單據中的每個明細在憑證中生成一條借方分錄和一條貸方分錄。將付款單中的付款科目作為借方科目,將付款單結算方式對應的結算方式科目作為貸方科目。現結的付款單在採購發票中已製作憑證了,因此不再單獨製作憑證。非預付的付款單不製作憑證,而是其執行付款核銷以後,在核銷單中製作憑證。”經過對以上語言的分析,我們可以繪製以下關係:一張憑證包含多個分錄,是內聚關係。分錄分為借方分錄和貸方分錄兩種。一條商品明細對應一條借方分錄和一條貸方分錄。借方分錄中包含“借方科目”屬性,對應付款單中的付款科目;貸方分錄中包含“貸方科目”屬性,對應的是付款單中的一個什麼科目。在這裡,你可能對客戶的描述不明白,因此要他做出解釋。原來客戶預先制訂了一個規則,付款單中的結算方式分佈對應了一個結算方式科目。OK,你在繪製的圖形中,把結算方式科目作為關聯類,將結算方式和貸方科目進行了一個關聯。這樣,“付款單生成憑證”這樣一個場景的領域模型就繪製出來(如圖)。


1.如何提取概念類
這是一個我們非常熟悉的情景劇,它為我們揭示了建立領域模型是怎樣開始,也就是說,我們是如何獲取領域模型所需素材的。獲取領域模型所需素材通常有兩個途徑:與客戶現場交流中獲得,和在用例的各個流程中提取名詞或名稱短語獲得,這些我們稱之為概念類。現在的問題是,哪些應當成為領域模型中的概念類呢?如果我引用一堆定義和準則,並不能讓你清楚明瞭,也許一個生動的比喻更能夠讓你理解深刻。需求分析有時候就像一部部動畫劇,而那些枯燥乏味的概念,紛繁複雜的流程,在這些動畫劇中似乎都突然活了,個個都有語言有性格。在這些動畫劇中扮演的所有角色,就是我們需要的概念類。而他們做的所有動作,就是用例模型中的所有流程。

另一個比較撓頭的問題就是,業務領域中的哪些概念應當成為概念類,哪些應對成為概念類中的屬性。這是一個非常模糊的問題,沒有一個準確的答案。一般來說,如果一個概念記錄的僅僅是一段文字或一個數字,那麼它應當作為一個概念類中的某個屬性,否則就應當作為一個概念類。比如“快遞員送快遞”,這裡的“快遞員”、“快遞”都是從中提取的概念類,但是“快遞”中的“地址”呢?這要看你現在分析的這個系統如何去記錄這個“地址”了。如果記錄的僅僅是一個文字,那麼它應當成為“快遞”中的一個“地址”屬性;如果記錄的不僅僅是一個文字,而是精細地記錄了“郵政編碼”、“城市”、“街道”、“通訊地址”等資訊,那麼它應當成為一個概念類,與“快遞”進行關聯。

除了這些名稱和名稱短語形成的概念類,還有一些相對獨立的行為,作為服務也應當形成概念類。這一類概念類我們可以在需求分析不斷深化的過程中,在以後的迭代中加入到領域模型。

2.建立關聯關係

除了提取概念類,領域模型還需要繪製出這些概念類相互之間的關係。由於領域模型是一個類圖,概念類是一個個的類,因此概念類之間的關係一般有三種:依賴、關聯和繼承。

依賴是類與類之間最普通的關係,它僅僅表示一個類(客戶類)瞭解它的供應者類,並且供應者類的變化會影響到客戶元素。依賴關係表現為,客戶類會建立、引用供應者類,或者供應者類是客戶類中的一個變數(引數變數、區域性變數、全域性變數或屬性變數)。沒有供應者類將會造成客戶類無法建立、無法使用,因此依賴是一種耦合關係。依賴在UML中被繪製成從客戶類指向供應者類的一條虛線箭頭。

關聯是依賴關係的一種特例,它代表供應者類是客戶類的一個屬性變數。關聯關係往往具有雙向性,如一個部門關係的關聯中,部門是員工類的一個屬性,表示某個員工的所屬於一個部門,而員工集合又是部門類的一個屬性,表示某個部門下都有哪些員工。但是,在我們研究的業務領域中,我們往往關心的是某個方向的關聯關係而忽視了另一個方向的關聯關係。如一個工資管理系統中,我們往往關心的是員工在哪個部門,而很少關心某個部門下有哪些人。在這種情況下,關聯關係表現為了一種導航,即員工對部門的導航。關聯關係在UML中表示成一條直線,而具有導航的關聯關係則表示成從被引用類指向引用類的實線箭頭,即員工指向部門的箭頭。

除了導航,關聯關係還表現為一對一、一對多、多對一和多對多關係。在繪製關聯關係的時候,我們通過在實線的兩端標註“1”或“*”來說明,這裡就不展開討論了。另外,我們還可以在關聯關係的中間用簡短的一個詞或短語,說明這是怎樣的一個關係,或者在關聯關係的兩端標註這兩個類分別代表的角色。

特別值得說明的是多對多關係。為了說明多對多關係的對應關係,往往還需要在關聯關係中新增一個關聯類。如使用者許可權模組的使用者與角色就是一個多對多關聯,因此在它們中間還需要增加一個“使用者與角色關係”類,表明哪些使用者與角色關聯,哪些角色與使用者關聯。
關聯關係是領域模型主要關注的一種關係。

聚合關係是關聯關係的特例,它除了表示一種具有導航的關聯關係外,還表示一種整體與部分的關係,如訂單與訂單明細、憑證與憑證分錄等。聚合表示為一段是黑色菱形的實線,黑色菱形端代表整體,另一端代表部分。聚合在以後的領域分析中佔有重要位置,我們回頭再講。組合是聚合的特例,它與聚合的唯一區別是,當代表整體的類被摧毀時,代表部分的類必須摧毀。由於組合涉及到了太多的技術實現,與領域模型的宗旨不符,我們往往很少去分析組合關係。

除此之外,領域模型還會出現繼承關係。由於領域模型中不可能出現介面,因此領域模型不可能出現實現關係。

3.領域模型的說明
在建立領域模型的時候一個非常重要的事情就是,一定要避免領域模型中的概念類出現二義性。在一次我與客戶討論需求的過程中,我和客戶都使用了一個業務術語,但我們對這個業務術語的理解存在著差異,以致我們花了大量時間來討論一個問題,卻誰也沒有向對方說明白自己的意思,直到最後我們發現對這個術語理解的偏差。這是一個反面的例子,說明避免二義性對溝通的重要。領域模型的說明,應當對一些重要詞彙或者業務術語進行必要解釋。

另外,領域模型的說明還有詳細解釋各個類之間的相互關係,特別是那些關聯關係,為日後的分析設計提供支援。