日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

DDD读书笔记

發(fā)布時間:2024/3/13 编程问答 73 豆豆
生活随笔 收集整理的這篇文章主要介紹了 DDD读书笔记 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

DDD筆記

  • 總體導覽圖
  • 分層模式
  • 軟件中的模型
    • 關聯(lián)
    • Entity
    • Value Object
    • Service
    • Module
  • 領域?qū)ο蟮纳芷?/li>
    • Aggregate
    • FACTORY
      • 選擇FACTORY及其應用位置
      • 有些情況下只需使用構(gòu)造函數(shù)
      • 接口的設計
      • 固定規(guī)則的相關邏輯應放置在哪里
      • ENTITY FACTORY與VALUE OBJECT FACTORY
      • 重建已存儲的對象
    • REPOSITORY
      • REPOSITORY與FACTORY的關系

總體導覽圖

分層模式

用戶界面層(或表示層)負責向用戶顯示信息和解釋用戶指令。這里指的用戶可以是另一個計算機系統(tǒng), 不一定是使用用戶界面的人
應用層定義軟件要完成的任務,并且指揮表達領域概念的對象來解決問題。這一層所負 責的工作對業(yè)務來說意義重大,也是與其他系統(tǒng)的應用層進行交互的必要渠道 。應用層要盡量簡單,不包含業(yè)務規(guī)則或者知識,而只為下一層中的領域?qū)ο髤f(xié)調(diào) 任務,分配工作,使它們互相協(xié)作。它沒有反映業(yè)務情況的狀態(tài),但是卻可以具有 另外一種狀態(tài),為用戶或程序顯示某個任務的進度
領域?qū)?或模型層)負責表達業(yè)務概念,業(yè)務狀態(tài)信息以及業(yè)務規(guī)則。盡管保存業(yè)務狀態(tài)的技術細節(jié) 是由基礎設施層實現(xiàn)的,但是反映業(yè)務情況的狀態(tài)是由本層控制并且使用的。領域 層是業(yè)務軟件的核心
基礎設施層為上面各層提供通用的技術能力:為應用層傳遞消息,為領域?qū)犹峁┏志没瘷C制, 為用戶界面層繪制屏幕組件,等等。基礎設施層還能夠通過架構(gòu)框架來支持4個層次 間的交互模式

軟件中的模型

關聯(lián)

對象之間的關聯(lián)使得建模與實現(xiàn)之間的交互更為復雜。
模型中每個可遍歷的關聯(lián),軟件中都要有同樣屬性的機制。

至少有3種方法可以使得關聯(lián)更易于控制。
(1) 規(guī)定一個遍歷方向。
(2) 添加一個限定符,以便有效地減少多重關聯(lián)。
(3) 消除不必要的關聯(lián)。

例子:

像很多國家一樣,美國有過很多位總統(tǒng)。這是一種雙向的、一對多的關系。然而,在提到?喬 治〃華盛頓?這個名字時,我們很少會問?他是哪個國家的總統(tǒng)??。從實用的角度講,我們可 83 以將這種關系簡化為從國家到總統(tǒng)的單向關聯(lián)。如圖5-1所示。這種精化實際上反映了對領域的 深入理解,而且也是一個更實用的設計。它表明一個方向的關聯(lián)比另一個方向的關聯(lián)更有意義且
更重要。也使得Person類不受非基本概念President的束縛。

通常,通過更深入的理解可以得到一個?限定的?關系。進一步研究總統(tǒng)的例子就可以知道, 一個國家在一段時期內(nèi)只能有一位總統(tǒng)(內(nèi)戰(zhàn)期間或許有例外)。這個限定條件把多重關系簡化 為一對一關系,并且在模型中植入了一條明確的規(guī)則。如圖5-2所示。1790年誰是美國總統(tǒng)?喬 治〃華盛頓。

Entity

定義 : 很多對象不是通過它們的屬性定義的,而是通過連續(xù)性和標識定義的。

一些對象主要不是由它們的屬性定義的。它們實際上表示了一條“標識線”(A Thread of Identity),這條線跨越時間,而且常常經(jīng)歷多種不同的表示。有時,這樣的對象必須與另一個具 有不同屬性的對象相匹配。而有時一個對象必須與具有相同屬性的另一個對象區(qū)分開。錯誤的標 識可能會破壞數(shù)據(jù)。

當一個對象由其標識(而不是屬性)區(qū)分時,那么在模型中應該主要通過標識來確定該對象 的定義。使類定義變得簡單,并集中關注生命周期的連續(xù)性和標識。定義一種區(qū)分每個對象的方 式,這種方式應該與其形式和歷史無關。要格外注意那些需要通過屬性來匹配對象的需求。在定 義標識操作時,要確保這種操作為每個對象生成唯一的結(jié)果,這可以通過附加一個保證唯一性的 符號來實現(xiàn)。這種定義標識的方法可能來自外部,也可能是由系統(tǒng)創(chuàng)建的任意標識符,但它在模 型中必須是唯一的標識。模型必須定義出“符合什么條件才算是相同的事物”。

比較
體育場座位預訂程序可能會將座位和觀眾當作ENTITY來處理。在分配座位時,每張票都有一 個座位號,座位是ENTITY。其標識符就是座位號,它在體育場中是唯一的。座位可能還有很多其 他屬性,如位臵、視野是否開闊、價格等,但只有座位號(或者說某一排的一個位臵)才用于識 別和區(qū)分座位。
另一方面,如果活動采用入場卷的方式,那么觀眾可以尋找任意的空座位來坐,這樣就不需 要對座位加以區(qū)分。在這種情況下,只有座位總數(shù)才是重要的。盡管座位上仍然印有座位號,但 軟件已經(jīng)不需要跟蹤它們。事實上,這時如果模型仍然將座位號與門票關聯(lián)起來,那么它就是錯 誤的,因為采用入場卷的活動并沒有這樣的約束。在這種情況下,座位不是ENTITY,因此不需要 標識符。

需要關注設計標識操作

Value Object

定義: 很多對象沒有概念上的標識,它們描述了一個事務的某種特征。

跟蹤ENTITY的標識是非常重要的,但為其他對象也加上標識會影響系統(tǒng)性能并增加分析工 作,而且會使模型變得混亂,因為所有對象看起來都是相同的。
軟件設計要時刻與復雜性做斗爭。我們必須區(qū)別對待問題,僅在真正需要的地方進行特殊 處理。
然而,如果僅僅把這類對象當作沒有標識的對象,那么就忽略了它們的工具價值或術語價 值。事實上,這些對象有其自己的特征,對模型也有著自己的重要意義。這些是用來描述事物 的對象。
用于描述領域的某個方面而本身沒有概念標識的對象稱為VALUE OBJECT(值對象)。VALUE OBJECT被實例化之后用來表示一些設計元素,對于這些設計元素,我們只關心它們是什么,而不 關心它們是誰。

當我們只關心一個模型元素的屬性時,應把它歸類為VALUE OBJECT。我們應該使這個模型 元素能夠表示出其屬性的意義,并為它提供相關功能。VALUE OBJECT應該是不可變的。不要為 它分配任何標識,而且不要把它設計成像ENTITY那么復雜。

關于關聯(lián):
如果說ENTITY之間的雙向關聯(lián)很難維護,那么兩個VALUE OBJECT之間的雙向關聯(lián)則完 全沒有意義。當一個VALUE OBJECT指向另一個VALUE OBJECT時,由于沒有標識,說一個對象指向的 3 對象正是那個指向它的對象并沒有任何意義的。我們充其量只能說,一個對象指向的對象與那個指 向它的對象是等同的,但這可能要求我們必須在某個地方實施這個固定規(guī)則。

Value Object 可以通過多種方式優(yōu)化,比如 共享 或者 復制, 一般 Value Object 設計為不可變的

Service

定義: 有時,對象不是一個事物。
在某些情況下,最清楚、最實用的設計會包含一些特殊的操作,這些操作從概念上講不屬于 任何對象。與其把它們強制地歸于哪一類,不如順其自然地在模型中引入一種新的元素,這就是 SERVICE(服務)。

  • 一些領域概念不適合被建模為對象。如果勉強把這些重要的領域功能歸為ENTITY或VALUE OBJECT的職責,那么不是歪曲了基于模型的對象的定義,就是人為地增加了一些無意義的對象

  • 好的SERVICE有以下3個特征。
    (1) 與領域概念相關的操作不是ENTITY或VALUE OBJECT的一個自然組成部分。
    (2) 接口是根據(jù)領域模型的其他元素定義的。
    (3) 操作是無狀態(tài)的。

  • 當領域中的某個重要的過程或轉(zhuǎn)換操作不是ENTITY或VALUE OBJECT的自然職責時,應該 在模型中添加一個作為獨立接口的操作,并將其聲明為SERVICE。定義接口時要使用模型語言, 并確保操作名稱是UBIQUITOUS LANGUAGE中的術語。此外,應該使SERVICE成為無狀態(tài)的

很多領域或應用層SERVICE是在ENTITY和VALUE OBJECT的基礎上建立起來的,它們的行為類 似于將領域的一些潛在功能組織起來以執(zhí)行某種任務的腳本。ENTITY和VALUE OBJECT往往由于粒度過細而無法提供對領域?qū)庸δ艿谋憬菰L問。我們在這里會遇到領域?qū)优c應用層之間很微妙的分 界線。例如,如果銀行應用程序可以把我們的交易進行轉(zhuǎn)換并導出到一個電子表格文件中,以便 進行分析,那么這個導出操作就是應用層SERVICE。?文件格式?在銀行領域中是沒有意義的,它 也不涉及業(yè)務規(guī)則。
另一方面,賬戶之間的轉(zhuǎn)賬功能屬于領域?qū)覵ERVICE,因為它包含重要的業(yè)務規(guī)則(如處理 相應的借方賬戶和貸方賬戶),而且?資金轉(zhuǎn)賬?是一個有意義的銀行術語。在這種情況下,SERVICE 自己并不會做太多的事情,而只是要求兩個Account對象完成大部分工作。但如果將轉(zhuǎn)賬操作強加在Account對象上會很別扭,因為這個操作涉及兩個賬戶和一些全局規(guī)則。

粒度 :
由于應用層負責對領域?qū)ο蟮男袨檫M行協(xié)調(diào),因此細粒度的領域?qū)ο罂赡軙杨I 域?qū)拥闹R泄漏到應用層中。這產(chǎn)生的結(jié)果是應用層不得不處理復雜的、細致的交互,從而使得 領域知識蔓延到應用層或用戶界面代碼當中,而領域?qū)訒G失這些知識。明智地引入領域?qū)臃?有助于在應用層和領域?qū)又g保持一條明確的界限。

Module

MODULE為人們提供了兩種觀察模型的方式,一是可以在MODULE中查看 細節(jié),而不會被整個模型淹沒,二是觀察MODULE之間的關系,而不考慮其內(nèi)部細節(jié)

MODULE之間應該是低耦合的,而在MODULE的內(nèi)部則是高內(nèi)聚的。耦合和內(nèi)聚的 解釋使得MODULE聽上去像是一種技術指標,仿佛是根據(jù)關聯(lián)和交互的分布情況來機械地判斷它 們。然而,MODULE并不僅僅是代碼的劃分,而且也是概念的劃分。一個人一次考慮的事情是有限 的(因此才要低耦合)。不連貫的思想和“一鍋粥”似的思想同樣難于理解(因此才要高內(nèi)聚)。

領域?qū)ο蟮纳芷?/h1>


領域?qū)ο笊芷谥饕魬?zhàn):
(1) 在整個生命周期中維護完整性。
(2) 防止模型陷入管理生命周期復雜性造成的困境當中。

AGGREGATE(聚合),它通過定義清晰的所屬關系和邊界,并避免混亂、錯綜復雜的對象關系網(wǎng)來實現(xiàn)模型的內(nèi)聚。聚合模式對于維護生命周期各 個階段的完整性具有至關重要的作用。
FACTORY(工廠)來創(chuàng)建和重建復 雜對象和AGGREGATE(聚合),從而封裝它們的內(nèi)部結(jié)構(gòu)。最后,在生命周期的中間和末尾使用 REPOSITORY(存儲庫)來提供查找和檢索持久化對象并封裝龐大基礎設施的手段。

使用AGGREGATE進行建模,并且在設計中結(jié)合使用FACTORY和REPOSITORY,這樣我們就能夠 在模型對象的整個生命周期中,以有意義的單元、系統(tǒng)地操縱它們。AGGREGATE可以劃分出一個 范圍,這個范圍內(nèi)的模型元素在生命周期各個階段都應該維護其固定規(guī)則。FACTORY和 REPOSITORY在AGGREGATE基礎上進行操作,將特定生命周期轉(zhuǎn)換的復雜性封裝起來。

Aggregate

減少設計中的關聯(lián)有助于簡化對象之間的遍歷,并在某種程度上限制關系的急劇增多。但大 多數(shù)業(yè)務領域中的對象都具有十分復雜的聯(lián)系,以至于最終會形成很長、很深的對象引用路徑, 我們不得不在這個路徑上追蹤對象。在某種程度上,這種混亂狀態(tài)反映了現(xiàn)實世界,因為現(xiàn)實世 界中就很少有清晰的邊界。但這卻是軟件設計中的一個重要問題。

定義:
AGGREGATE就是一組相關對象的集合,我們把它作為數(shù)據(jù)修改的單元。每個AGGREGATE都有一個根(root)和一個邊界(boundary)。邊界 定義了AGGREGATE的內(nèi)部都有什么。根則是AGGREGATE所包含的一個特定ENTITY。對AGGREGATE 而言,外部對象只可以引用根,而邊界內(nèi)部的對象之間則可以互相引用。除根以外的其他ENTITY 都有本地標識,但這些標識只在AGGREGATE內(nèi)部才需要加以區(qū)別,因為外部對象除了根ENTITY之 外看不到其他對象。

例子:
汽車修配廠的軟件可能會使用汽車模型。汽車是一個具有全局標識的ENTITY: 我們需要將這部汽車與世界上所有其他汽車區(qū)分開(即使是一些非常相似的汽車)。我們可以使 用車輛識別號來進行區(qū)分,車輛識別號是為每輛新汽車分配的唯一標識符。我們可能想通過4個 輪子的位臵跟蹤輪胎的轉(zhuǎn)動歷史。我們可能想知道每個輪胎的里程數(shù)和磨損度。要想知道哪個輪 胎在哪兒,必須將輪胎標識為ENTITY。當脫離這輛車的上下文后,我們很可能就不再關心這些輪胎的標識了。如果更換了輪胎并將舊輪胎送到回收廠,那么軟件將不再需要跟蹤它們,它們會成 為一堆廢舊輪胎中的一部分。沒有人會關心它們的轉(zhuǎn)動歷史。更重要的是,即使輪胎被安在汽車 上,也不會有人通過系統(tǒng)查詢特定的輪胎,然后看看這個輪胎在哪輛汽車上。人們只會在數(shù)據(jù)庫 中查找汽車,然后臨時查看一下這部汽車的輪胎情況。因此,汽車是AGGREGATE的根ENTITY,而 輪胎處于這個AGGREGATE的邊界之內(nèi)。另一方面,發(fā)動機組上面都刻有序列號,而且有時是獨立 于汽車被跟蹤的。在一些應用程序中,發(fā)動機可以是自己的AGGREGATE的根。

固定規(guī)則:
固定規(guī)則(invariant)是指在數(shù)據(jù)變化時必須保持的一致性規(guī)則,其涉及AGGREGATE成員之 間的內(nèi)部關系。而任何跨越AGGREGATE的規(guī)則將不要求每時每刻都保持最新狀態(tài)。通過事件處理、 批處理或其他更新機制,這些依賴會在一定的時間內(nèi)得以解決。但在每個事務完成時,AGGREGATE 內(nèi)部所應用的固定規(guī)則必須得到滿足

  • 根ENTITY具有全局標識,它最終負責檢查固定規(guī)則
  • 根ENTITY具有全局標識。邊界內(nèi)的ENTITY具有本地標識,這些標識只在AGGREGATE內(nèi)部才是唯一的。
  • AGGREGATE外部的對象不能引用除根ENTITY之外的任何內(nèi)部對象。根ENTITY可以把對內(nèi)部ENTITY的引用傳遞給它們,但這些對象只能臨時使用這些引用,而不能保持引用。根 可以把一個VALUE OBJECT的副本傳遞給另一個對象,而不必關心它發(fā)生什么變化,因為它只是一個VALUE,不再與AGGREGATE有任何關聯(lián)。
  • 作為上一條規(guī)則的推論,只有AGGREGATE的根才能直接通過數(shù)據(jù)庫查詢獲取。所有其他對象必須通過遍歷關聯(lián)來發(fā)現(xiàn)。
  • AGGREGATE內(nèi)部的對象可以保持對其他AGGREGATE根的引用。
  • 刪除操作必須一次刪除AGGREGATE邊界之內(nèi)的所有對象。(利用垃圾收集機制,這很容易做到。由于除根以外的其他對象都沒有外部引用,因此刪除了根以后,其他對象均會被回收。)
  • 當提交對AGGREGATE邊界內(nèi)部的任何對象的修改時,整個AGGREGATE的所有固定規(guī)則都必須被滿足。

我們應該將ENTITY和VALUE OBJECT分門別類地聚集到AGGREGATE中,并定義每個 AGGREGATE的邊界。在每個AGGREGATE中,選擇一個ENTITY作為根,并通過根來控制對邊界內(nèi) 其他對象的所有訪問。只允許外部對象保持對根的引用。對內(nèi)部成員的臨時引用可以被傳遞出去, 但僅在一次操作中有效。由于根控制訪問,因此不能繞過它來修改內(nèi)部對象。這種設計有利于確 保AGGREGATE中的對象滿足所有固定規(guī)則,也可以確保在任何狀態(tài)變化時AGGREGATE作為一個 整體滿足固定規(guī)則。

有一個能夠聲明AGGREGATE的技術框架是很有幫助的,這樣就可以自動實施鎖機制和其他一 些功能。如果沒有這樣的技術框架,團隊就必須靠自我約束來使用事先商定的AGGREGATE,并按 照這些AGGREGATE來編寫代碼。

FACTORY

當創(chuàng)建一個對象或創(chuàng)建整個AGGREGATE時,如果創(chuàng)建工作很復雜,或者暴露了過多的內(nèi)部結(jié) 構(gòu),則可以使用FACTORY進行封裝。

對象的功能主要體現(xiàn)在其復雜的內(nèi)部配臵以及關聯(lián)方面。我們應該一直對對象進行提煉,直 到所有與其意義或在交互中的角色無關的內(nèi)容被完全剔除為止。一個對象在它的生命周期中要承 擔大量職責。如果再讓復雜對象負責自身的創(chuàng)建,那么職責過載將會導致問題。

比如:

汽車發(fā)動機是一種復雜的機械裝臵,它由數(shù)十個零件共同協(xié)作來履行發(fā)動機的職責——使軸 轉(zhuǎn)動。我們可以試著設計一種發(fā)動機組,讓它自己抓取一組活塞并塞到汽缸中,火花塞也可以自 己找到插孔并把自己擰進去。但這樣組裝的復雜機器可能沒有我們常見的發(fā)動機那樣可靠或高 效。相反,我們用其他東西來裝配發(fā)動機?;蛟S是機械師,或者是工業(yè)機器人。無論是機器人還 是人,實際上都比二者要裝配的發(fā)動機復雜。裝配零件的工作與使軸旋轉(zhuǎn)的工作完全無關。只是 在生產(chǎn)汽車時才需要裝配工,我們駕駛時并不需要機器人或機械師。由于汽車的裝配和駕駛永遠不會同時發(fā)生,因此將這兩種功能合并到同一個機制中是毫無價值的。同理,裝配復雜的復合對 象的工作也最好與對象要執(zhí)行的工作分開。
但將職責轉(zhuǎn)交給另一個相關方——應用程序中的客戶(client)對象——會產(chǎn)生更嚴重的問 題。客戶知道需要完成什么工作,并依靠領域?qū)ο髞韴?zhí)行必要的計算。如果指望客戶來裝配它需 要的領域?qū)ο?#xff0c;那么它必須要了解一些對象的內(nèi)部結(jié)構(gòu)。為了確保所有應用于領域?qū)ο蟾鞑糠株P 系的固定規(guī)則得到滿足,客戶必須知道對象的一些規(guī)則。甚至調(diào)用構(gòu)造函數(shù)也會使客戶與所要構(gòu) 建的對象的具體類產(chǎn)生耦合。結(jié)果是,對領域?qū)ο髮崿F(xiàn)所做的任何修改都要求客戶做出相應修改, 這使得重構(gòu)變得更加困難。
當客戶負責創(chuàng)建對象時,它會牽涉不必要的復雜性,并將其職責搞得模糊不清。這違背了領 域?qū)ο蠹八鶆?chuàng)建的AGGREGATE的封裝要求。更嚴重的是,如果客戶是應用層的一部分,那么職責 就會從領域?qū)有孤┑綉脤又?。應用層與實現(xiàn)細節(jié)之間的這種耦合使得領域?qū)映橄蟮拇蟛糠謨?yōu)勢 蕩然無存,而且導致后續(xù)更改的代價變得更加高昂。

對象的創(chuàng)建本身可以是一個主要操作,但被創(chuàng)建的對象并不適合承擔復雜的裝配操作。將這 些職責混在一起可能產(chǎn)生難以理解的拙劣設計。讓客戶直接負責創(chuàng)建對象又會使客戶的設計陷入 混亂,并且破壞被裝配對象或AGGREGATE的封裝,而且導致客戶與被創(chuàng)建對象的實現(xiàn)之間產(chǎn)生 過于緊密的耦合。

因此:
應該將創(chuàng)建復雜對象的實例和AGGREGATE的職責轉(zhuǎn)移給單獨的對象,這個對象本身可能沒有 承擔領域模型中的職責,但它仍是領域設計的一部分。提供一個封裝所有復雜裝配操作的接口, 而且這個接口不需要客戶引用要被實例化的對象的具體類。在創(chuàng)建AGGREGATE時要把它作為一 個整體,并確保它滿足固定規(guī)則。

*任何好的工廠都需滿足以下兩個基本需求。

(1) 每個創(chuàng)建方法都是原子的,而且要保證被創(chuàng)建對象或AGGREGATE的所有固定規(guī)則。 FACTORY生成的對象要處于一致的狀態(tài)。在生成ENTITY時,這意味著創(chuàng)建滿足所有固定規(guī)則的整 個AGGREGATE,但在創(chuàng)建完成后可以向聚合添加可選元素。在創(chuàng)建不變的VALUE OBJECT時,這意 味著所有屬性必須被初始化為正確的最終狀態(tài)。如果FACTORY通過其接口收到了一個創(chuàng)建對象的 請求,而它又無法正確地創(chuàng)建出這個對象,那么它應該拋出一個異常,或者采用其他機制,以確 保不會返回錯誤的值。

(2) FACTORY應該被抽象為所需的類型,而不是所要創(chuàng)建的具體類。[Gamma et al. 1995]中的 高級FACTORY模式介紹了這一話題。*

選擇FACTORY及其應用位置

一般來說,FACTORY的作用是隱藏創(chuàng)建對象的細節(jié),而且我們把FACTORY用在那些需要隱藏 細節(jié)的地方。這些決定通常與AGGREGATE有關。

  • 如果需要向一個已存在的AGGREGATE添加元素,可以在AGGREGATE的根上創(chuàng)建一個 FACTORY METHOD。這樣就可以把AGGREGATE的內(nèi)部實現(xiàn)細節(jié)隱藏起來,使任何外部客戶看不到 這些細節(jié),同時使根負責確保AGGREGATE在添加元素時的完整性
  • 另一種情況是:在一個對象上使用FACTORY METHOD,這個對象與生成另一個對象密切相關, 但它并不擁有所生成的對象。當一個對象的創(chuàng)建主要使用另一個對象的數(shù)據(jù)(或許還有規(guī)則)時, 則可以在后者的對象上創(chuàng)建一個FACTORY METHOD,這樣就不必將后者的信息提取到其他地方來創(chuàng)建前者。這樣做還有利于表達前者與后者之間的關系。
  • FACTORY與被構(gòu)建對象之間是緊密耦合的,因此FACTORY應該只被關聯(lián)到與被構(gòu)建對象有著密 切聯(lián)系的對象上。當有些細節(jié)需要隱藏(無論要隱藏的是具體實現(xiàn)還是構(gòu)造的復雜性)而又找不 到合適的地方來隱藏它們時,必須創(chuàng)建一個專用的FACTORY對象或SERVICE。整個AGGREGATE通常 由一個獨立的FACTORY來創(chuàng)建,FACTORY負責把對根的引用傳遞出去,并確保創(chuàng)建出的AGGREGATE 滿足固定規(guī)則。如果AGGREGATE內(nèi)部的某個對象需要一個FACTORY,而這個FACTORY又不適合在 AGGREGATE根上創(chuàng)建,那么應該構(gòu)建一個獨立的FACTORY。但仍應遵守規(guī)則——把訪問限制在 AGGREGATE內(nèi)部,并確保從AGGREGATE外部只能對被構(gòu)建對象進行臨時引用

有些情況下只需使用構(gòu)造函數(shù)

在有些情況下直接使用構(gòu)造函數(shù)確實是最佳選擇。FACTORY實際上會使那些不具 有多態(tài)性的簡單對象復雜化。

在以下情況下最好使用簡單的、公共的構(gòu)造函數(shù):

  • 類(class)是一種類型(type)。它不是任何相關層次結(jié)構(gòu)的一部分,而且也沒有通過接口實現(xiàn)多態(tài)性。
  • 客戶關心的是實現(xiàn),可能是將其作為選擇STRATEGY的一種方式。
  • 客戶可以訪問對象的所有屬性,因此向客戶公開的構(gòu)造函數(shù)中沒有嵌套的對象創(chuàng)建。
  • 構(gòu)造并不復雜。
  • 公共構(gòu)造函數(shù)必須遵守與FACTORY相同的規(guī)則:它必須是原子操作,而且要滿足被創(chuàng)建對
    象的所有固定規(guī)則。

不要在構(gòu)造函數(shù)中調(diào)用其他類的構(gòu)造函數(shù)。構(gòu)造函數(shù)應該保持絕對簡單。復雜的裝配,特別是AGGREGATE,需要使用FACTORY。使用FACTORY METHOD的門檻并不高。
Java類庫提供了一些有趣的例子。所有集合都實現(xiàn)了接口,接口使得客戶與具體實現(xiàn)之間不 產(chǎn)生耦合。然而,它們都是通過直接調(diào)用構(gòu)造函數(shù)創(chuàng)建的。但是,集合類本來是可以使用FACTORY來封裝集合的層次結(jié)構(gòu)的。而且,客戶也可以使用FACTORY的方法來請求所需的特性,然后由 FACTORY來選擇適當?shù)念悂韺嵗_@樣一來,創(chuàng)建集合的代碼就會有更強的表達力,而且新增 集合類時不會破壞現(xiàn)有的Java程序。
但在某些場合下使用具體的構(gòu)造函數(shù)更為合適。首先,在很多應用程序中,實現(xiàn)方式的選擇 對性能的影響是非常敏感的,因此應用程序需要控制選擇哪種實現(xiàn)(盡管如此,真正智能的 FACTORY仍然可以滿足這些因素的要求)。不管怎樣,集合類的數(shù)量并不多,因此選擇并不復雜。
雖然沒有使用FACTORY,但抽象集合類型仍然具有一定價值,原因就在于它們的使用模式。 集合通常都是在一個地方創(chuàng)建,而在其他地方使用。這意味著最終使用集合(添加、刪除和檢索 其內(nèi)容)的客戶仍可以與接口進行對話,從而不與實現(xiàn)發(fā)生耦合。集合類的選擇通常由擁有該集
142 合的對象來決定,或是由該對象的FACTORY來決定。

接口的設計

當設計FACTORY的方法簽名時,無論是獨立的FACTORY還是FACTORY METHOD,都要記住以下 兩點:

  • 每個操作都必須是原子的
  • Factory將與其參數(shù)發(fā)生耦合
  • 最安全的參數(shù)是那些來自較低設計層的參數(shù)。即使在同一層中,也有一種自然的分層傾向, 其中更基本的對象被更高層的對象使用
  • 另一個好的參數(shù)選擇是模型中與被構(gòu)建對象密切相關的對象,這樣不會增加新的依賴。
  • 使用抽象類型的參數(shù),而不是它們的具體類。FACTORY與被構(gòu)建對象的具體類發(fā)生耦合,而無需與具體的參數(shù)發(fā)生耦合。

固定規(guī)則的相關邏輯應放置在哪里

FACTORY負責確保它所創(chuàng)建的對象或AGGREGATE滿足所有固定規(guī)則,然而在把應用于一個對象的規(guī)則移到該對象外部之前應三思。FACTORY可以將固定規(guī)則的檢查工作委派給被創(chuàng)建對象, 而且這通常是最佳選擇。

雖然原則上在每個操作結(jié)束時都應該應用固定規(guī)則,但通常對象所允許的轉(zhuǎn)換可能永遠也不 會用到這些規(guī)則。可能ENTITY標識屬性的賦值需要滿足一條固定規(guī)則。但該標識在創(chuàng)建后可能一 直保持不變。VALUE OBJECT則是完全不變的。如果邏輯在對象的有效生命周期內(nèi)永遠也不被用到, 那么對象就沒有必要攜帶這個邏輯。 在這種情況下,FACTORY是放臵固定規(guī)則的合適地方,這樣 可以使FACTORY創(chuàng)建出的對象更簡單。

ENTITY FACTORY與VALUE OBJECT FACTORY

由于VALUE OBJECT是不可變 的,因此,FACTORY所生成的對象就是最終形式。因此FACTORY操作必須得到被創(chuàng)建對象的完整 描述。而ENTITY FACTORY則只需具有構(gòu)造有效AGGREGATE所需的那些屬性。對于固定規(guī)則不關心 的細節(jié),可以之后再添加。

我們來看一下為ENTITY分配標識時將涉及的問題(VALUE OBJECT不會涉及這些問題)。正如 第5章所指出的那樣,既可以由程序自動分配一個標識符,也可以通過外部(通常是用戶)提供 一個標識符。如果客戶的標識是通過電話號碼跟蹤的,那么該電話號碼必須作為參數(shù)被顯式地傳 遞給FACTORY。當由程序分配標識符時,FACTORY是控制它的理想場所。盡管唯一跟蹤ID實際上 144 是由數(shù)據(jù)庫“序列”或其他基礎設施機制生成的,但FACTORY知道需要什么樣的標識,以及將標 識放到何處。

重建已存儲的對象

用于重建對象的FACTORY與用于創(chuàng)建對象的FACTORY很類似,主要有以下兩點不同:

  • 用于重建對象的ENTITY FACTORY不分配新的跟蹤ID。如果重新分配ID,將丟失與先前對 象的連續(xù)性。因此,在重建對象的FACTORY中,標識屬性必須是輸入?yún)?shù)的一部分。
  • 當固定規(guī)則未被滿足時,重建對象的FACTORY采用不同的方式進行處理:
    當創(chuàng)建新對象時,如果未滿足固定規(guī)則,FACTORY應該簡單地拒絕創(chuàng)建對象,但在重建對象時則需要更靈活的響應。如果對象已經(jīng)在系統(tǒng)的某個地方存在(如在數(shù)據(jù)庫中),那么不能忽略這個事實。但是, 同樣也不能任憑規(guī)則被破壞。必須通過某種策略來修復這種不一致的情況,這使得重建對象比創(chuàng) 建新對象更困難。
  • REPOSITORY

    背景: 我們可以通過對象之間的關聯(lián)來找到對象。但當它處于生命周期的中間時,必須要有一個起 點,以便從這個起點遍歷到一個ENTITY或VALUE。
    無論要用對象執(zhí)行什么操作,都需要保持一個對它的引用。那么如何獲得這個引用呢?一種 方法是創(chuàng)建對象,因為創(chuàng)建操作將返回對新對象的引用。第二種方法是遍歷關聯(lián)。我們以一個已 知對象作為起點,并向它請求一個關聯(lián)的對象。這樣的操作在任何面向?qū)ο蟮某绦蛑卸紩罅坑?到,而且對象之間的這些鏈接使對象模型具有更強的表達能力。但我們必須首先獲得作為起點的 那個對象。

    客戶需要一種有效的方式來獲取對已存在的領域?qū)ο蟮囊?。如果基礎設施提供了這方面的 便利,那么開發(fā)人員可能會增加很多可遍歷的關聯(lián),這會使模型變得非?;靵y。另一方面,開發(fā) 人員可能使用查詢從數(shù)據(jù)庫中提取他們所需的數(shù)據(jù),或是直接提取具體的對象,而不是通過 AGGREGATE的根來得到這些對象。這樣就導致領域邏輯進入查詢和客戶代碼中,而ENTITY和 VALUE OBJECT則變成單純的數(shù)據(jù)容器。采用大多數(shù)處理數(shù)據(jù)庫訪問的技術復雜性很快就會使客 戶代碼變得混亂,這將導致開發(fā)人員簡化領域?qū)?#xff0c;最終使模型變得無關緊要。**

    目標: 根據(jù)到目前為止所討論的設計原則,如果我們找到一種訪問方法,它能夠明確地將模型作為 焦點,從而應用這些原則,那么我們就可以在某種程度上縮小對象訪問問題的范圍

    初學者可 以不必關心臨時對象。臨時對象(通常是VALUE OBJECT)只存在很短的時間,在客戶操作中用到 它們時才創(chuàng)建它們,用完就刪除了。我們也不需要對那些很容易通過遍歷來找到的持久對象進行 查詢訪問。例如,地址可以通過Person對象獲取。而且最重要的是,除了通過根來遍歷查找對象 這種方法以外,禁止用其他方法對AGGREGATE內(nèi)部的任何對象進行訪問。
    持久化的VALUE OBJECT一般可以通過遍歷某個ENTITY來找到,在這里ENTITY就是把對象封
    裝在一起的AGGREGATE的根。事實上,對VALUE的全局搜索訪問常常是沒有意義的,因為通過屬 性找到VALUE OBJECT相當于用這些屬性創(chuàng)建一個新實例。但也有例外情況。例如,當我在線規(guī)劃 旅行線路時,有時會先保存幾個中意的行程,過后再回頭從中選擇一個來預訂。這些行程就是 VALUE(如果兩個行程由相同的航班構(gòu)成,那么我不會關心哪個是哪個),但它們已經(jīng)與我的用 戶名關聯(lián)到一起了,而且可以原封不動地將它們檢索出來。另一個例子是“枚舉”,在枚舉中一 個類型有一組嚴格限定的、預定義的可能值。但是,對VALUE OBJECT的全局訪問比對ENTITY的全 局訪問更少見,如果確實需要在數(shù)據(jù)庫中搜索一個已存在的VALUE,那么值得考慮一下,搜索結(jié) 果可能實際上是一個ENTITY,只是尚未識別它的標識。

    現(xiàn)在可以更精確地將問題重新表述如下:
    _在所有持久化對象中,有一小部分必須通過基于對象屬性的搜索來全局訪問。當很難通過遍 歷方式來訪問某些AGGREGATE根的時候,就需要使用這種訪問方式。它們通常是ENTITY,有時 是具有復雜內(nèi)部結(jié)構(gòu)的VALUE OBJECT,還可能是枚舉VALUE。而其他對象則不宜使用這種訪問方 式,因為這會混淆它們之間的重要區(qū)別。隨意的數(shù)據(jù)庫查詢會破壞領域?qū)ο蟮姆庋b和 AGGREGATE。技術基礎設施和數(shù)據(jù)庫訪問機制的暴露會增加客戶的復雜度,并妨礙模型驅(qū)動的 設計。

    有得必有失,我們應該注意失去了什么。我們已經(jīng)不再考慮領域模型中的概念。代碼也不再 表達業(yè)務,而是對數(shù)據(jù)庫檢索技術進行操縱。REPOSITORY是一個簡單的概念框架,它可用來封裝 這些解決方案,并將我們的注意力重新拉回到模型上。_

    因此:
    為每種需要全局訪問的對象類型創(chuàng)建一個對象,這個對象相當于該類型的所有對象在內(nèi)存中 的一個集合的“替身”。通過一個眾所周知的全局接口來提供訪問。提供添加和刪除對象的方法,
    151 用這些方法來封裝在數(shù)據(jù)存儲中實際插入或刪除數(shù)據(jù)的操作。提供根據(jù)具體條件來挑選對象的方 法,并返回屬性值滿足查詢條件的對象或?qū)ο蠹?所返回的對象是完全實例化的),從而將實 際的存儲和查詢技術封裝起來。只為那些確實需要直接訪問的AGGREGATE根提供REPOSITORY。 讓客戶始終聚焦于模型,而將所有對象的存儲和訪問操作交給REPOSITORY來完成。

    REPOSITORY有很多優(yōu)點,包括:

    • 它們?yōu)榭蛻籼峁┝艘粋€簡單的模型,可用來獲取持久化對象并管理它們的生命周期;
    • 它們使應用程序和領域設計與持久化技術(多種數(shù)據(jù)庫策略甚至是多個數(shù)據(jù)源)解耦;
    • 它們體現(xiàn)了有關對象訪問的設計決策;
    • 可以很容易將它們替換為“啞實現(xiàn)”(dummy implementation),以便在測試中使用(通常
      使用內(nèi)存中的集合)。

    實現(xiàn):

    • 對類型進行抽象。REPOSITORY“含有”特定類型的所有實例,但這并不意味著每個類都 需要有一個REPOSITORY。類型可以是一個層次結(jié)構(gòu)中的抽象超類(例如,TradeOrder可以 是BuyOrder或SellOrder)。類型可以是一個接口——接口的實現(xiàn)者并沒有層次結(jié)構(gòu)上的關 聯(lián),也可以是一個具體類。記住,由于數(shù)據(jù)庫技術缺乏這樣的多態(tài)性質(zhì),因此我們將面 臨很多約束。
    • 充分利用與客戶解耦的優(yōu)點。 我們可以很容易地更改REPOSITORY的實現(xiàn),但如果客戶直接調(diào)用底層機制,我們就很難修改其實現(xiàn)。也可以利用解耦來優(yōu)化性能,因為這樣就可以使用不同的查詢技術,或在內(nèi)存中緩存對象,可以隨時自由地切換持久化策略。通過 提供一個易于操縱的、內(nèi)存中的(in-memory)啞實現(xiàn),還能夠方便客戶代碼和領域?qū)ο?br /> 的測試。
    • 將事務的控制權(quán)留給客戶。 盡管REPOSITORY會執(zhí)行數(shù)據(jù)庫的插入和刪除操作,但它通常不會提交事務。例如,保存數(shù)據(jù)后緊接著就提交似乎是很自然的事情,但想必只有客戶 才有上下文,從而能夠正確地初始化和提交工作單元。如果REPOSITORY不插手事務控制, 那么事務管理就會簡單得多。

    REPOSITORY與FACTORY的關系

    從領域驅(qū)動設計的角度來看,FACTORY 和REPOSITORY具有完全不同的職責。FACTORY負責制造新對象,而REPOSITORY負責查找已有對象。 REPOSITORY應該讓客戶感覺到那些對象就好像駐留在內(nèi)存中一樣。對象可能必須被重建(的確, 可能會創(chuàng)建一個新實例),但它是同一個概念對象,仍舊處于生命周期的中間。

    REPOSITORY也可以委托FACTORY來創(chuàng)建一個對象,這種方法(雖然實際很少這樣做,但在理 論上是可行的)可用于從頭開始創(chuàng)建對象,此時就沒有必要區(qū)分這兩種看問題的角度了

    這種職責上的明確區(qū)分還有助于FACTORY擺脫所有持久化職責。FACTORY的工作是用數(shù)據(jù)來 實例化一個可能很復雜的對象。如果產(chǎn)品是一個新對象,那么客戶將知道在創(chuàng)建完成之后應該把 它添加到REPOSITORY中,由REPOSITORY來封裝對象在數(shù)據(jù)庫中的存儲:

    總結(jié)

    以上是生活随笔為你收集整理的DDD读书笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。