二 領域驅動設計-運用領域模型-交流與語言的使用

来源:https://www.cnblogs.com/zhijiancanxue/archive/2020/03/18/12520958.html

[toc] 運用領域模型 交流與語言的使用 非原創,感謝《領域驅動設計》這本書 領域模型可成為軟體項目通用語言的核心。該模型是一組得自於項目人員頭腦中的概念,以及反映了領域深層含義的術語和關係。這些術語和相互關係提供了模型語言的語義,雖然語言是為領域量身定製的,但就技術開發而言,其依然足夠精確。正是 ...


運用領域模型-交流與語言的使用

非原創,感謝《領域驅動設計》這本書

領域模型可成為軟體項目通用語言的核心。該模型是一組得自於項目人員頭腦中的概念,以及反映了領域深層含義的術語和關係。這些術語和相互關係提供了模型語言的語義,雖然語言是為領域量身定製的,但就技術開發而言,其依然足夠精確。正是這條至關重要的紐帶,將模型與開發活動結合在一起,並使模型與代碼緊密綁定。這種基於模型的交流並不局限於UML(統一建模語言)圖。為了最有效地使用模型,需要充分利用各種交流手段。基於模型的交流提高了書面文檔的效用,也提高了敏捷過程中再度強調的非正式圖表和交談的效用。它還通過代碼本身及對應的測試促進了交流。

UBIQUITOUS LANGUAGE(通用語言)

雖然領域專家對軟體開發的技術術語所知有限,但他們能熟練使用自己領域的術語——可能還具有各種不同的風格。另一方面,開發人員可能會用一些描述性的、功能性的術語來理解和討論系統,而這些術語並不具備領域專家的語言所要傳達的意思。或者,開發人員可能會創建一些用於支持設計的抽象,但領域專家無法理解這些抽象。負責處理問題不同部分的開發人員可能會開發出各自不同的設計概念以及描述領域的方式.

由於語言上存在鴻溝,領域專家們只能模糊地描述他們想要的東西。開發人員雖然努力去理解一個自己不熟悉的領域,但也只能形成模糊的認識。雖然少數團隊成員會設法掌握這兩種語言,但他們會變成信息流的瓶頸,並且他們的翻譯也不准確。(溝通非常重要)

在一個沒有公共語言的項目上,開發人員不得不為領域專家做翻譯。而領域專家需要充當開發人員與其他領域專家之間的翻譯。甚至開發人員之間還需要互相翻譯。這些翻譯使模型概念變得混淆,而這會導致有害的代碼重構。這種間接的溝通掩蓋了分裂的形成——不同的團隊成員使用不同的術語而尚不自知。由於軟體的各個部分不能夠渾然一體,因此這就導致無法開發出可靠的軟體。翻譯工作導致各類促進深入理解模型的知識和想法無法結合到一起。

日常討論所使用的術語與代碼(軟體項目的最重要產品)中使用的術語不一致。甚至同一個人在講話和寫東西時使用的語言也不一致,這導致的後果是,對領域的深刻表述常常稍縱即逝,根本無法記錄到代碼或文檔中。翻譯使得溝通不暢,並削弱了知識消化。

然而任何一方的語言都不能成為公共語言,因為它們無法滿足所有的需求。所有翻譯的開銷,連帶著誤解的風險,成本實在太高了。項目需要一種公共語言,這種語言要比所有語言的最小公分母健壯得多。通過團隊的一致努力,領域模型可以成為這種公共語言的核心,同時將團隊溝通與軟體實現緊密聯繫到一起。該語言將存在於團隊工作中的方方面面。

個人理解:上面說法太絕對了,主要是為了突出通用語言的重要性。不必在意。

UBIQUITOUS LANGUAGE(通用語言)的辭彙包括類和主要操作的名稱。語言中的術語,有些用來討論模型中已經明確的規則,還有一些則來自施加於模型上的高級組織原則。最後,團隊常常應用於領域模型的模式名稱也使這種語言更為豐富。

開發人員應該使用基於模型的語言來描述系統中的工件、任務和功能。這個模型應該為開發人員和領域專家提供一種用於相互交流的語言,而且領域專家還應該使用這種語言來討論需求、開發計劃和特性。語言使用得越普遍,理解進行得就越順暢。

至少,我們應該將它作為目標。但最初,模型可能不太好,因此無法很好地履行這些職責。它可能不會像領域的專業術語那樣具有豐富的語義。但我們又不能直接使用那些術語,因為它們有歧義和矛盾。模型可能缺乏開發人員在代碼中所創建的更為微妙和靈活的特性,這要麼是因為開發人員認為模型不必具備這些特性,要麼是因為編碼風格是過程式的,只能隱含地表達領域概念。

儘管模型和基於模型的語言之間的次序像是迴圈論證,但是,能夠產生更有用模型的知識消化過程依賴於團隊投身於基於模型的語言。持續使用UBIQUITOUS LANGUAGE可以暴露模型中存在的缺點,這樣團隊就可以嘗試並替換不恰當的術語或組合。當在語言中發現缺失時,新的詞語將被引入到討論中。這些語言上的更改也會在領域模型中引起相應的更改,並促使團隊更新類圖並重命名代碼中的類和方法,當術語的意義改變時,甚至會導致行為也發生改變。

個人理解:儘管模型難以很快的理解,有可能一個團隊用了很久才達成共識形成一套自己理解的模型,但是對於剛剛加入的人來說就像天書,無疑增加的學習成本,如果模型更新過程中,有些術語發生變更(隨著理解的深入),會導致功能重做。

將模型作為語言的支柱。確保團隊在內部的所有交流中以及代碼中堅持使用這種語言。在畫圖、寫東西,特別是講話時也要使用這種語言。通過嘗試不同的表示方法(它們反映了備選模型)來消除難點。然後重構代碼,重新命名類、方法和模塊,以便與新模型保持一致。解決交談中的術語混淆問題,就像我們對普通辭彙形成一致的理解一樣。要認識到,UBIQUITOUS LANGUAGE的更改就是對模型的更改。領域專家應該抵制不合適或無法充分表達領域理解的術語或結構,開發人員應該密切關註那些將會妨礙設計的有歧義和不一致的地方。有了UBIQUITOUS LANGUAGE,模型就不僅僅是一個設計工件了。它成為開發人員和領域專家共同完成的每項工作中不可或缺的部分。語言以動態形式傳遞知識。使用這種語言進行討論能夠呈現圖和代碼背後的真實含義。

“大聲地”建模

假如將交談從溝通方式中除去的話,那會是巨大的損失,因為人類本身頗具談話的天賦。遺憾的是,當人們交談時,通常並不使用領域模型的語言。

可能開始時你並不認為上述論斷是正確的,而且的確有例外情況。但下次你參加需求或設計討論時,不妨認真聽一下。你將聽到人們用業務術語或者各種業餘術語來描述功能。還會聽到人們討論技術工件和具體的功能。當然,你還會聽到來自領域模型的術語;在人們共同使用的那部分業務術語中,那些顯而易見的名詞在編碼時通常被用作對象名稱,因此這些術語經常被人們提及。但你是否也聽到一些使用當前領域模型中的關係和交互來描述的措辭呢?

改善模型的最佳方式之一就是通過對話來研究,試著大聲說出可能的模型變化中的各種結構。這樣不完善的地方很容易被聽出來。

例如:“如果我們向Routing Service提供出發地、目的地和到達時間,就可以查詢貨物的停靠地點,嗯……將它們存到資料庫中。”(含糊且偏重於技術);“出發地、目的地……把它們都輸入到Routing Service中,而後我們得到一個Itinerary,它包含我們所需的全部信息。”(更具體,但過於啰嗦);“Routing Service查找滿足Route Specification的Itinerary。”(簡潔)

個人理解:簡潔、抽象、完整的表達,就是模型的描述語言。儘量不說白話,類似文言文。如果剛開始使用模型語言不習慣,也要在溝通時候去適應,大膽說出來,一起適應,形成習慣。

討論系統時要結合模型。使用模型元素及其交互來大聲描述場景,並且按照模型允許的方式將各種概念結合到一起。找到更簡單的表達方式來講出你要講的話,然後將這些新的想法應用到圖和代碼中。

一個團隊,一種語言

技術人員通常認為業務專家最好不要接觸領域模型,他們認為:

“領域模型對他們來說太抽象了。”

“他們不理解對象。”

“這樣我們就不得不用他們的術語來收集需求。”

當然,設計中有一些技術組件與領域專家無關,但模型的核心最好讓他們參與。過於抽象?那你怎麼知道抽象是否合理?你是否像他們一樣深入理解領域?有時,某些特定需求是從底層用戶那裡收集的,他們在描述這些需求時可能會用到一小部分更具體的術語,但領域專家應該能夠更深入地思考他們所從事的領域。如果連經驗豐富的領域專家都不能理解模型,那麼模型一定出了什麼問題。

最初,當用戶討論系統尚未建模的未來功能時,他們沒有模型可供使用。但當他們開始與開發人員一起仔細討論這些新想法時,探索共用模型的過程就開始了。最初的模型可能很笨拙且不完整,但會逐漸精化。隨著新語言的演進,領域專家必須付出更多努力來適應它,並更新那些仍然很重要的舊文檔。當領域專家使用這種語言互相討論,或者與開發人員進行討論時,很快就會發現模型中哪些地方不符合他們的需要,甚至是錯誤的。另一方面,模型語言的精確性也會促使領域專家(在開發人員的幫助下)發現他們想法中的矛盾和含糊之處。

個人理解:不要擔心業務領域專家聽不懂技術設計的模型語言,大家可以一起來討論,共同探索,業務人員也要主動去適應,一旦理解後,可以提出自己的意見,幫助改進模型。總的來說,使用模型就是好,有困難-剋服。

開發人員和領域專家可以通過一步一步地使用模型對象來走查場景,從而對模型進行非正式的測試。每次討論都是開發人員和專家一起使用模型的機會,在這個過程中,他們可以加深彼此的理解,並對概念進行精化。領域專家可以使用模型語言來編寫用例,甚至可以直接利用模型來具體說明驗收測試。

開發人員的確會使用領域專家無法理解的技術術語。開發人員有其所需的大量術語來討論系統技術。幾乎可以肯定的是,用戶也會用開發人員無法理解的、超出應用程式範疇的專用術語。這些都是對語言的擴展。但在這些語言擴展中,同一領域的相同辭彙不應該反映不同的模型。有了UBIQUITOUS LANGUAGE之後,開發人員之間的對話、領域專家之間的討論以及代碼本身所表達的內容都基於同一種語言,都來自於一個共用的領域模型。

文檔和圖

每當我參加討論軟體設計的會議時,如果不在白板或畫板上畫圖,我就很難討論下去。我畫的大部分是UML圖,主要以類圖和對象交互圖為主。

有些人天生是視覺動物,圖可以幫助人們掌握某些類型的信息。UML圖在傳達對象之間的關係上真是游刃有餘,而且也很擅長表現交互。但它們卻無法給出這些對象的概念定義。在會議中,我會一邊畫圖一邊用語言來豐富它們的意義,或者在與其他參與者討論時進行解釋。簡單、非正式的UML圖能夠維繫整個討論。繪製一幅包含當前問題最關鍵的3~5個對象的圖,這樣每個人都可以集中註意力。所有人就對象關係會達成一致的認識,更重要的是,他們將使用相同的對象名稱。如此,口頭討論會更加高效。當人們嘗試不同的想法時,圖也隨之改變,草圖在某種程度上可以反映討論的變化,這是討論中真正重要的部分。畢竟,UML就是統一建模語言。

當人們必須通過UML圖表示整個模型或設計時,麻煩也隨之而來。很多對象模型圖在某些方面過於細緻,同時在某些方面又有很多遺漏。說它們過於細緻是因為人們認為必須將所有要編碼的對象都放到建模工具中。而細節過多的結果是“只見樹木,不見森林”。

個人理解:我認為uml也可以說是模型的一種表達形式,隨便在白板上劃也是一種模型,只不過不那麼規範,比較隨意

UML也不是一種十分令人滿意的編程語言。我從未見過有人使用建模工具的代碼生成功能達到了預期目的。如果UML的能力無法滿足需要,通常人們就不得不忽略模型最關鍵的部分,因為有些規則並不適合用線框圖來表示。當然,代碼生成器也無法使用上面所說的那些文本註釋。如果確實能使用UML這樣的繪圖語言來編寫可執行程式,那麼UML圖就會退化為程式本身的另一種視圖,這樣,“模型”的真正含義就丟失了。如果使用UML作為實現語言,則仍然需要利用其他手段來表達模型的確切含義。

圖是一種溝通和解釋手段,它們可以促進頭腦風暴。簡潔的小圖能夠很好地實現這些目標,而涵蓋整個對象模型的綜合性大圖反而失去了溝通或解釋能力,因為它們將讀者淹沒在大量細節之中,加之這些圖也缺乏目的性。鑒於此,我們應避免使用包羅萬象的對象模型圖,甚至不能使用包含所有細節的UML數據存儲庫。相反,應使用簡化的圖,圖中只包含對象模型的重要概念——這些部分對於理解設計至關重要。本書中的圖都是我在項目中使用過比較典型的圖。它們很簡單,而且具有很強的解釋能力,在澄清一些要點時,還使用了一些非標準的符號。它們顯示了設計約束,但它們不是面面俱到的設計規範。它們只體現了思想綱要。

設計的重要細節應該在代碼中體現出來。良好的實現應該是透明的,清楚地展示其背後的模型。互為補充的圖和文檔能夠引導人們將註意力放在核心要點上。自然語言的討論可以填補含義上的細微差別。這就是為什麼我喜歡把典型的UML使用方法顛倒過來的原因。通常的用法是以圖為主,輔以文本註釋;而我更願意以文本為主,用精心挑選的簡化圖作為說明。

模型不是圖。圖的目的是幫助表達和解釋模型。代碼可以充當設計細節的存儲庫。書寫良好的Java代碼與UML具有同樣的表達能力。經過仔細選擇和構造的圖可以幫助人們集中註意力,並起到指導作用,當然前提條件是不能強制用圖來表示全部模型或設計,因為這樣會削弱圖的清晰表達的能力。

書面設計文檔

口頭交流可以解釋代碼的含義,因此可作為代碼精確性和細節的補充。雖然交談對於將人們與模型聯繫起來是至關重要的,但書面文檔也是必不可少的,任何規模的團隊都需要它來提供穩定和共用的交流。但要想編寫出能夠幫助團隊開發出好軟體的書面文檔卻是一個不小的挑戰。

一句話:好記性不如爛筆頭。文檔應作為代碼和口頭交流的補充

每種敏捷過程在編寫文檔方面都有自己的理念。極限編程主張完全不使用(多餘的)設計文檔,而讓代碼解釋自己。實際運行的代碼不會說謊,而其他文檔則不然。運行代碼所產生的行為是明確的。

極限編程就是敏捷開發,敏捷開發不提倡寫太多文檔,能省就省,代碼多些註釋,代替文檔。

極限編程只關註對程式及可執行測試起作用的因素。由於為代碼添加的註釋並不影響程式的行為,因此它們往往無法與當前代碼及其模型保持同步。外部文檔和圖也不會影響程式的行為,因此它們也無法保持同步。另一方面,口頭交流和臨時在白板上畫的圖不會長久保留而產生混淆。依賴代碼作為交流媒介可以促使開發人員保持代碼的整潔和透明。

將代碼作為設計文檔也有局限性。它可能會把讀代碼的人淹沒在細節中。儘管代碼的行為是非常明確的,但這並不意味著其行為是顯而易見的。就算技術人員可以看懂代碼,專業領域的業務人員怎麼辦???所以,文檔還是有必要的,實事求是來制定開發方式和文檔編寫方式。

文檔應當鮮活並保持最新。設計文檔的最大價值在於解釋模型的概念,幫助在代碼的細節中指引方向,或許還可以幫助人們深入瞭解模型預期的使用風格。根據不同的團隊理念,整個設計文檔可能會十分簡單,如只是貼在牆上的一組草圖,也可能會非常詳盡。

完全依賴可執行代碼的情況

良好的代碼具有很強的表達能力,但它所傳遞的信息不能確保是準確的。一段代碼所產生的實際行為是不會改變的。但是,方法名稱可能會有歧義、會產生誤導或者因為已經過時而無法表示方法的本質含義。變數和代碼組織方式所表達出來的意思未必嚴格。好的編程風格會儘力使這種聯繫直接化,但其仍然主要靠開發人員的自律。編碼時需要一絲不苟的態度,只有這樣才能編寫出“言行全部正確”的代碼。

解釋性模型(重點來了,記好)

在實現、設計和團隊交流中使用同一個模型作為基礎。如果各有各的模型,將會造成危害。

模型在幫助領域學習方面也具有很大價值。對設計起到推動作用的模型是領域的一個視圖,但為了學習領域,還可以引入其他視圖,這些視圖只用作傳遞一般領域知識的教學工具。出於此目的,人們可以使用與軟體設計無關的其他種類模型的圖片或文字。

驅動軟體開發過程的技術模型必須經過嚴格的精簡,以便用最小化的模型來實現其功能。而解釋性模型則可以包含那些提供上下文的領域方面——這些上下文用於澄清範圍更窄的模型。

解釋性模型提供了一定的自由度,可以專門為某個特殊主題定製一些表達力更強的風格。領域專家在一個領域中所使用的視覺隱喻通常呈現了更清晰的解釋,這可以教給開發人員領域知識,同時使領域專家們的意見更一致。解釋性模型還可以以一種不同的方式來呈現領域,並且各種不同角度的解釋有助於人們更好地學習。

解釋性模型不必是對象模型,而且最好不是。實際上在這些模型中不使用UML是有好處的,這樣可以避免人們錯誤地認為這些模型與軟體設計是一致的。儘管解釋性模型與驅動設計的模型往往有對應關係,但它們並不完全類似。為了避免混淆,每個人都必須知道它們之間的區別。

例如:

考慮一個用來追蹤航運公司貨物的應用程式。模型包含一個詳細的視圖,它顯示瞭如何將港口裝卸和貨輪航次組合為一次貨運的操作計劃:

但對外行而言,類圖可能起不到多大的說明作用。

在這種情況下,解釋性模型可以幫助團隊成員理解類圖的實際含義。圖2-5是表示相同概念的另一種方式。

圖中的每根線段都表示貨物的一種狀態——或者正在港口裝卸(裝貨或卸貨),或者停放在倉庫里,或者正在運輸途中。這個圖並沒有與類圖中的細節一一對應,但強調了領域的要點。這種圖連同對它所表示的模型的自然語言解釋,能夠幫助開發人員和領域專家理解更嚴格的軟體模型圖。綜合使用這兩種圖要比單獨使用一種圖更容易理解。


您的分享是我們最大的動力!

更多相關文章
  • JavaScript 中函數原型是實現繼承的基礎。prototype、construct、原型鏈以及基於原型鏈的繼承是面向對象的重要內容 ...
  • 故事背景: 沙雕在公司一個開放API團隊工作,平時的工作內容主要是做一些對外API項目開發,負責跟第三方系統對接。雖然工作也幾年了,但是一直都是按照上級做好的設計文檔和分配的任務去做,很少自己去做設計和思考。有一天團隊老大跑路了,部門老闆想提拔他做團隊老大的工作,但需要負責一個新項目架構設計工作來試 ...
  • 策略模式(Strategy):它定義了演算法家族,分別封裝起來,讓它們之間可以互相替換,此模式讓演算法的變化不會影響到使用演算法的客戶。 ——《大話設計模式》 策略模式主要用來解決當有多種相似演算法的時,使用if...else產生的難以維護的問題。它主要由三部分組成:Strategy介面、具體的Strate ...
  • 圖解Java設計模式之橋接模式 手機操作問題 傳統方案解決手機操作問題 傳統方案解決手機操作問題分析 橋接模式(Bridge)-基本介紹 橋接模式解決手機操作問題 橋接模式在JDBC中的源碼解析 橋接模式的註意事項和細節 橋接模式其它應用場景 手機操作問題 現在對不同手機類型的不同品牌實現操作編程( ...
  • [toc] 領域驅動設計 領域對象的生命周期 每個對象都有生命周期,如圖6 1所示。對象自創建後,可能會經歷各種不同的狀態,直至最終消亡——要麼存檔,要麼刪除。當然,很多對象是簡單的臨時對象,僅通過調用構造函數來創建,用來做一些計算,而後由垃圾收集器回收。這類對象沒必要搞得那麼複雜。但有些對象具有更 ...
  • [toc] 軟體中所表示的模型 表示模型的3種模型元素模式:ENTITY、VALUE OBJECT和SERVICE。從錶面上看,定義那些用來捕獲領域概念的對象很容易,但要想反映其含義卻很困難。這要求我們明確區分各種模型元素的含義,並與一系列設計實踐結合起來,從而開發出特定類型的對象。 個人理解:就是 ...
  • [toc] 模型驅動設計的構造塊 分離領域 在軟體中,雖然專門用於解決領域問題的那部分通常只占整個軟體系統的很小一部分,但其卻出乎意料的重要。我們需要著眼於模型中的元素並且將它們視為一個系統。絕不能像在夜空中辨認星座一樣,被迫從一大堆混雜的對象中將領域對象挑選出來。我們需要將領域對象與系統中的其他功 ...
  • [toc] 領域驅動設計 運用領域模型 綁定模型和實現 聰明的項目組成員花費了幾個月的時間進行仔細的研究並且開發出了詳盡的領域模型(類圖)。然而對類圖研究不能讓我深入地瞭解該應用程式的代碼和設計,這讓我備感困擾。當開發人員開始實現應用程式時,他們很快就發現,儘管分析人員說得頭頭是道,他們依然無法將這 ...
一周排行
  • 前幾天發佈了 "抄抄《CSS 故障藝術》的動畫" 這篇文章,在這篇文章里介紹瞭如何使用Win2D繪製文字然後配合BlendEffect製作故障藝術的動畫。本來打算就這樣收手不玩這個動畫了,但後來又發現性能不符合理想。明明只是做做Resize動畫和用BlendEffect混合,為什麼性能會這麼差呢? ...
  • 控制條控制項: progressBar 不能按照你程式的進程自動變化,需認為計算,調整變化量 private void progressBar1_Click(object sender, EventArgs e) { this.progressBar1.Maximum = 100;//設置進度條最大長 ...
  • 首先創建一個asp.net core web應用程式 第二步 目前官方預置了7種模板項目供我們選擇。從中我們可以看出,既有我們熟悉的MVC、WebAPI,又新添加了Razor Page,以及結合比較流行的Angular、React前端框架的模板項目。 空項目模板 Program.cs using S ...
  • 對閉包的理解 1.對於成員變數和局部變數:成員變數就是方法外部,類的內部定義的變數;局部變數就是方法或語句塊內部定義的變數。局部變數必須初始化。 形式參數是局部變數,局部變數的數據存在於棧記憶體中。棧記憶體中的局部變數隨著方法的消失而消失。成員變數存儲在堆中的對象裡面,由垃圾回收器負責回收。 成員變數它 ...
  • Xamarin.Forms讀取並展示Android和iOS通訊錄 TerminalMACS客戶端 本文同步更新地址: https://dotnet9.com/11520.html https://terminalmacs.com/861.html 閱讀導航: 一、功能說明 二、代碼實現 三、源碼獲取 ...
  • 做下對文件複製操作相關的筆記: /// <summary> /// 文件幫助類 /// </summary> public class FileHelper { /// <summary> /// 複製一個目錄下所有文件到一個新目錄下 /// </summary> /// <param name=" ...
  • 前言 有一個東西叫做鴨子類型,所謂鴨子類型就是,只要一個東西表現得像鴨子那麼就能推出這玩意就是鴨子。 C 裡面其實也暗藏了很多類似鴨子類型的東西,但是很多開發者並不知道,因此也就沒法好好利用這些東西,那麼今天我細數一下這些藏在編譯器中的細節。 不是只有 和 才能 在 C 中編寫非同步代碼的時候,我們經 ...
  • [toc] 1.應用背景 底端設備有大量網路報文(位元組數組):心跳報文,數據採集報文,告警報文上報。需要有對應的報文結構去解析這些位元組流數據。 2.結構體解析 由此,我第一點就想到了用結構體去解析。原因有以下兩點: 2.1.結構體存在棧中 類屬於引用類型,存在堆中;結構體屬於值類型,存在棧中,在一個 ...
  • 《深入淺出 C#》 (第3版) [作者] (美) Andrew Stellman (美) Jennifer Greene[譯者] (中) 徐陽 丁小峰 等譯[出版] 中國電力出版社[版次] 2016年08月 第1版[印次] 2018年04月 第4次 印刷[定價] 148.00元 【引子】 要學習編程 ...
  • 記錄使用對象初始值設定項初始化對象。 using System; using System.Collections.Generic; namespace ConsoleApp2 { class Program { static void Main(string[] args) { // 使用構造函數 ...
x