領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和開(kāi)發(fā)實(shí)戰(zhàn)_第1頁(yè)
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和開(kāi)發(fā)實(shí)戰(zhàn)_第2頁(yè)
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和開(kāi)發(fā)實(shí)戰(zhàn)_第3頁(yè)
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和開(kāi)發(fā)實(shí)戰(zhàn)_第4頁(yè)
領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和開(kāi)發(fā)實(shí)戰(zhàn)_第5頁(yè)
已閱讀5頁(yè),還剩12頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶(hù)提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和開(kāi)發(fā)實(shí)戰(zhàn)背景領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)DDD)的中心內(nèi)容是如何將業(yè)務(wù)領(lǐng)域概念映射到軟件工件中。大部 分關(guān)丁此主題的著作和文章都以Eric Evans的書(shū) 領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)為基礎(chǔ),主要從概 念和設(shè)計(jì)的角度探討領(lǐng)域建模和設(shè)計(jì)情況。這些著作討論實(shí)體、值對(duì)象、服務(wù)等 DDD的主要內(nèi)容,或者談?wù)撏ㄓ谜Z(yǔ)言、界定的上下文Bounded Context)和咖層Anti-Corruption Layer )這些的概念。本文旨在從實(shí)踐的角度探討領(lǐng)域建模和設(shè)計(jì),涉及如何著手處理領(lǐng)域模型并實(shí)際 地實(shí)現(xiàn)它。我們將著眼丁技術(shù)主管和架極帥在實(shí)現(xiàn)過(guò)程中能用到的指 導(dǎo)方針、最佳 實(shí)踐、框架及工具。領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和開(kāi)發(fā)也受一些架極、設(shè)

2、計(jì)、實(shí)現(xiàn)方面的影響,比 如:業(yè)務(wù)規(guī)則持久化緩存 事務(wù)管理安全代碼生成測(cè)試驅(qū)動(dòng)開(kāi)發(fā) 重構(gòu)本文討論這些不同的因素在 項(xiàng)目實(shí)施的整個(gè)生命周期中怎 樣對(duì)其產(chǎn)生影響,還有 架極帥在實(shí)現(xiàn)成功的DDD中應(yīng)該去尋求什么。我會(huì)先列出領(lǐng)域模型應(yīng)該具備的典 型特征,以及何時(shí)在企業(yè)中使用領(lǐng)域模型3四丁根本不使用領(lǐng)域模型,或使用貧 血的領(lǐng)域模型來(lái)說(shuō))。文章包括一個(gè)貸款處理示例應(yīng)用,來(lái)演示如何將設(shè)計(jì)立場(chǎng)、以及這里討論的開(kāi)發(fā)最 佳實(shí)踐,應(yīng)用在真實(shí)的領(lǐng)域驅(qū)動(dòng)開(kāi)發(fā)項(xiàng)目之中。示例應(yīng)用用了一些框架去 實(shí) 現(xiàn)貸 款 處理領(lǐng)域模型,比如 Spring、Dozer、Spring Security、JAXB、Arid POJOs 和 Sp

3、ring Dynamic Modules示例代碼用Java編寫(xiě),侶對(duì)大多數(shù)開(kāi)發(fā)人員來(lái)說(shuō),不論語(yǔ) 言背景如何,代碼都是很容易理解的。引言領(lǐng)域模型帶來(lái)了一些好處,其中有: 有助于團(tuán)隊(duì)創(chuàng) 建一個(gè)業(yè)務(wù)部門(mén)與IT部門(mén)都能理解的通用模型,并用 該模型來(lái)溝通 業(yè) 務(wù)需求、數(shù)據(jù) 實(shí)體、過(guò)程模型。 模型是模塊化、可擴(kuò)展、易于維護(hù)的,同時(shí)設(shè)計(jì)還反映了業(yè)務(wù)模型。 提高了業(yè)務(wù)領(lǐng)域?qū)ο蟮目芍赜眯院涂?測(cè)性。反過(guò)來(lái),如果IT團(tuán)隊(duì)在開(kāi)發(fā)大中型企業(yè)軟件應(yīng)用時(shí)不遵循領(lǐng)域模型方法,我們看看 會(huì)發(fā)生些什么。不投放資源去建立和開(kāi)發(fā)領(lǐng)域模型,會(huì)導(dǎo)致應(yīng)用架極出現(xiàn)“肥月盼層”和貧血的領(lǐng) 域模型”,假樣的架極中,外觀(guān)類(lèi)(通常是無(wú)狀態(tài)會(huì)話(huà)Be

4、an)開(kāi)始 積聚越 來(lái)越多 的業(yè)務(wù)邏輯,域?qū)ο髣t成為只有g(shù)etter和setter方法的數(shù)據(jù) 載體。這種做法還 會(huì)導(dǎo)致領(lǐng)域特定業(yè)務(wù)邏輯和規(guī)則散布丁多個(gè)的外觀(guān)類(lèi)中(有些情況下還會(huì)出現(xiàn)重 復(fù)的邏輯)。在大多數(shù)情況下,貧血的領(lǐng)域模型沒(méi)有成本效益;它們不會(huì)給公司帶來(lái)超越其它公 司的競(jìng)爭(zhēng)優(yōu)勢(shì),電在這種架極里要實(shí)現(xiàn)業(yè)務(wù)需求變更,開(kāi)發(fā)并部署到生產(chǎn)環(huán)境中 去要花費(fèi)太長(zhǎng)的時(shí)間。在考慮DDD實(shí)現(xiàn)的項(xiàng)目中各種架極和設(shè)計(jì)因素之前,讓我們先看看富領(lǐng)域模型的 特性:領(lǐng)域模型應(yīng)該側(cè)重于具體的業(yè)務(wù)操作領(lǐng)域。它應(yīng)該結(jié)合業(yè)務(wù)模型、策略和 業(yè)務(wù)流程。它應(yīng)該與業(yè)務(wù)中的其它領(lǐng)域,還有應(yīng)用架構(gòu)中的其它層隔離開(kāi)來(lái)。它應(yīng)該可重用,以避免相同

5、的核心 業(yè)務(wù)領(lǐng)域元素有任何重 復(fù)的模型和實(shí)現(xiàn)。模型應(yīng)該設(shè)計(jì)得與應(yīng)用中的其它層松耦合,這意味著領(lǐng)域?qū)优c上下兩層(即數(shù)據(jù)庫(kù)和 外觀(guān)類(lèi))都沒(méi)有依賴(lài)關(guān)系。它應(yīng)當(dāng)是一個(gè)抽象的、清晰劃分的 層次,以使 維護(hù)、測(cè)試、版本處理更容易。可在容 器外(從IDE中)對(duì)領(lǐng)域類(lèi)進(jìn)行單元測(cè)試。它應(yīng)該用POJO編程模型來(lái) 設(shè)計(jì),沒(méi)有任何技 術(shù)或框架依 賴(lài)性(我總是告訴公司里我 工作的項(xiàng)目團(tuán)隊(duì),我們軟件開(kāi)發(fā)用的技術(shù)是Java )。領(lǐng)域模型應(yīng)該獨(dú)立于持久化實(shí)現(xiàn)的細(xì)節(jié)(盡管技術(shù)確實(shí)會(huì)對(duì)模型有一些限制)。它應(yīng)該最小程度地依 賴(lài)于任何基 礎(chǔ)設(shè)施框架,因 為它將比這些框架更 經(jīng)久,我們也不 希望與任何外部框架緊耦合。為了實(shí)現(xiàn)軟件開(kāi)發(fā)中

6、更高的投資回報(bào)率ROI)業(yè)務(wù)單位和IT的高級(jí)管理人員必須 在業(yè)務(wù)領(lǐng)域建模及其實(shí)現(xiàn)的投資上 時(shí)間、做和資源)全力以赴。讓我們來(lái)看看實(shí) 現(xiàn)領(lǐng)域模型需要的其它因素。團(tuán)隊(duì)?wèi)?yīng)該經(jīng) 常接近業(yè)務(wù)領(lǐng)域主題專(zhuān)家。 IT團(tuán)隊(duì)(建模者、架構(gòu) 師和開(kāi)發(fā)人員)應(yīng)具備良好的建模、設(shè)計(jì)技能。分析師應(yīng)該具有良好的 業(yè)務(wù)流程建模技能。 架構(gòu)師和開(kāi)發(fā)人員應(yīng)該有豐富的面向 對(duì)象設(shè)計(jì)(OOD)和編程(OOP )經(jīng)驗(yàn)。領(lǐng)域驅(qū)動(dòng)設(shè)計(jì) 在企業(yè)架構(gòu)中的作用領(lǐng)域建模和DDD在企業(yè)架極EA)核揮著重要的作用。因?yàn)镋A的目標(biāo)之一就是 結(jié)合IT和業(yè)務(wù)部門(mén),業(yè)務(wù)實(shí)體的代表一一 域模型就是EA的核心部分。這就是為什 么大多數(shù)EA組件 業(yè)務(wù)或基礎(chǔ)設(shè)施)應(yīng)

7、該圍繞領(lǐng)域模型設(shè)計(jì)和實(shí)現(xiàn)的原因。領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和SOA面向服務(wù)的體系架極(SOA)最近幫助團(tuán)隊(duì)極建基丁業(yè)務(wù)流程的軟件極件和服務(wù)、 加速新產(chǎn)品上市時(shí)間的勢(shì)頭越來(lái)越強(qiáng)勁。領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)是SOA的一個(gè)關(guān)鍵因素, 因?yàn)樗兄》庋b領(lǐng)域?qū)ο笾械臉I(yè)務(wù)邏輯和規(guī)則。領(lǐng)域模型也提供了定 義服務(wù)契 約使用的語(yǔ)言和上下文。如果還沒(méi)有領(lǐng)域模型,SOA的實(shí)行就應(yīng)該包括領(lǐng)域模型的設(shè)計(jì)和實(shí)現(xiàn)。如果我們太 過(guò)強(qiáng)調(diào)SOA服務(wù)、忽略了領(lǐng)域模型的重要性,那我們?cè)趹?yīng)用架極中最終得到的就 是一個(gè)貧血的領(lǐng)域模型和臃腫的服務(wù)。理想的情況是,在開(kāi)發(fā)應(yīng)用層和SOA組件的同時(shí),迭代地實(shí)現(xiàn)DDD,因?yàn)閼?yīng)用層和 SOA組件都是領(lǐng)域模型要素的直接消 費(fèi)者

8、。使用豐富的領(lǐng)域?qū)崿F(xiàn),通過(guò)給領(lǐng) 域?qū)?象提供一個(gè)殼(代理),SOA設(shè)計(jì)將變得相對(duì)簡(jiǎn)單。但如果我們太過(guò)丁關(guān)注SOA層, 在后端卻沒(méi)有一個(gè)像樣的領(lǐng)域模型,業(yè)務(wù)服務(wù)就會(huì)調(diào)用不完整的領(lǐng)域模 型,這可 能會(huì)導(dǎo)致出現(xiàn)一個(gè)脆弱的SOA架極。項(xiàng)目管理領(lǐng)域建模項(xiàng)目通常包括以下 步驟:首先為業(yè)務(wù)流程建模并文檔化。選擇一個(gè)候選的業(yè)務(wù)流程,與 業(yè)務(wù)領(lǐng)域?qū)<乙黄鹗褂猛ㄓ?語(yǔ)言來(lái)文檔化 業(yè)務(wù)流程。識(shí)別候選業(yè)務(wù)流程需要的所有服 務(wù)。這些服務(wù)本質(zhì)上可以是原子的(單步的)或組合 好的(多步的,有無(wú)工作流皆可)。它們也可以是業(yè)務(wù)(比如承?;?資金)或基礎(chǔ)設(shè)施 (比如電子郵件或工作調(diào)度)。.對(duì)上一步識(shí)別的服務(wù)所使用的對(duì)象,確定并文

9、檔化其狀 態(tài)和行為。一開(kāi)始關(guān)注業(yè)務(wù)領(lǐng)域核心元素的時(shí)候,就將模型保持在高水平是非常重要的。從項(xiàng)目管理的觀(guān)點(diǎn)來(lái)看,真實(shí)的DDD實(shí)現(xiàn)項(xiàng)目和其它軟件開(kāi)發(fā)項(xiàng)目所包含的階段 是一樣的。這些階段包括:對(duì)領(lǐng)域進(jìn)行建模設(shè)計(jì)開(kāi)發(fā)單元測(cè)試和集成測(cè)試基于設(shè)計(jì)和開(kāi)發(fā)來(lái)完善、重構(gòu) 領(lǐng)域模型(模型概念的持 續(xù)集成(CI)。 使用更新的 領(lǐng)域模型重 復(fù)上述步驟(領(lǐng)域?qū)崿F(xiàn)的CI )。非常適合在這里使用敏捷軟件開(kāi)發(fā)方法學(xué),因?yàn)槊艚莘椒ㄗ⒅囟〗桓渡?業(yè)價(jià)值,恰 好DDD側(cè)重丁結(jié)合軟件系統(tǒng)和業(yè)務(wù)模型。此外,就DDD迭代的特性來(lái) 說(shuō),SCRUM 或DSDM這樣的敏捷方法對(duì)項(xiàng)目管理來(lái)說(shuō)也是更好的框架。結(jié)合使用SCRUM (適 用丁項(xiàng)目管

10、理)和XP (適用"件開(kāi)發(fā)目標(biāo))方痢處理DDD實(shí)現(xiàn)項(xiàng)目來(lái)說(shuō)非常 好。DDD迭代周期的項(xiàng)目管理模型如圖1所示。DDD lirplemefitatiQn CycleIUD.圖1. DDD迭代周期圖(庶擊查看大圖)領(lǐng)域建模結(jié)束時(shí)可以開(kāi)始領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)。關(guān)丁如何開(kāi)始實(shí)現(xiàn)領(lǐng)域?qū)ο竽P停?Ramnivas Laddad推薦如下的步驟。他強(qiáng)調(diào)要更側(cè)重丁領(lǐng)域模型中的領(lǐng)域?qū)ο?,?不是服務(wù)。 從領(lǐng)域?qū)嶓w和領(lǐng)域邏輯開(kāi)始。不要一開(kāi)始就從服務(wù)層開(kāi)始,只添加那些 邏輯不屬于任何 領(lǐng)域?qū)嶓w或值對(duì)象的服務(wù)。利用通用語(yǔ)言、契約式設(shè)計(jì)(DbC )、自動(dòng)化測(cè)試、CI和重構(gòu),使實(shí)現(xiàn)盡可能地與領(lǐng) 域模型緊密結(jié)合。從設(shè)計(jì)和實(shí)現(xiàn)

11、的角度來(lái)看,典型的DDD框架應(yīng)該支持以下特征。 應(yīng)該是一個(gè)以POJO (如果你的公司以.Net為主營(yíng),就是POCO )為基礎(chǔ)的架構(gòu)。 應(yīng)該支持使用DDD概念的業(yè)務(wù)領(lǐng)域模型的設(shè)計(jì)和實(shí)現(xiàn)。 應(yīng)該支持像依 賴(lài)注入(DI)和面向方向 編程(AOP )這些概念的開(kāi)箱即用。(注:稍 后將在文章中 詳細(xì)解釋這些概念)。. 與單元測(cè)試框架整合,比如 JUnit、TestNG、Unitils 等。 與其它Java/Java EE 框架進(jìn)行良好的集成,比如 JPA、Hibernate、TopLink 等。示例應(yīng)用本文中使用的示例 應(yīng)用是一個(gè)住房貸款處理系統(tǒng),業(yè)務(wù)用例是批準(zhǔn)住房貸款(抵 押)幽金申請(qǐng)。將貸款申請(qǐng)?zhí)峤?/p>

12、給抵押放貸公司的 時(shí)候,首先要通過(guò)承保過(guò)程, 承保人在這一過(guò)程中根據(jù)客戶(hù)的收入詳情、信用歷史記錄和其它因素來(lái)決定批準(zhǔn) 還是拒絕貸款請(qǐng)求。如果貸款申請(qǐng)獲得承保組的批準(zhǔn),就進(jìn)入貸款審批程序的結(jié) 活和融資步驟。貸款處理系統(tǒng)中的融資模塊自動(dòng)給貸款人支付資金。通常,謎資過(guò)程從抵押放貸公 司(通常是銀行)彳偵款包遞交給產(chǎn)權(quán)公司開(kāi)始。接著產(chǎn)權(quán)公司評(píng)估貸款包,并與房 產(chǎn)買(mǎi)賣(mài)雙方一起確定結(jié)活貸款的時(shí)間。貸款人和賣(mài)方與結(jié)算中介在產(chǎn)權(quán)公司會(huì)面、 簽署書(shū)面協(xié)議,來(lái)轉(zhuǎn)移房產(chǎn)產(chǎn)權(quán)。架構(gòu)典型的企業(yè)應(yīng)用架極由下面四個(gè)概念上的 層組成:用戶(hù)界面(表現(xiàn)層):負(fù)責(zé)給用戶(hù)展示信息,并解 釋用戶(hù)命令。 應(yīng)用層:該層協(xié)調(diào)應(yīng)用程序的活動(dòng)。

13、不包括任何業(yè)務(wù)邏輯,不保存業(yè)務(wù)對(duì)象的狀態(tài), 但能保存應(yīng)用程序任 務(wù)過(guò)程的狀態(tài)。領(lǐng)域?qū)樱哼@一層包括業(yè)務(wù)領(lǐng)域的信息。業(yè)務(wù)對(duì)象的狀態(tài)在這里保存。業(yè)務(wù)對(duì)象的持久 化和它們的狀態(tài)可能會(huì)委托給基礎(chǔ)設(shè)施層?;A(chǔ)設(shè)施層:對(duì)其它層來(lái)說(shuō),這一層是一個(gè)支持性的 庫(kù)。它提供 層之間的信息傳遞, 實(shí)現(xiàn)業(yè)務(wù)對(duì) 象的持久化,包含 對(duì)用戶(hù)界面層的支持性庫(kù)等。讓我們更詳細(xì)地看一下應(yīng)用層和領(lǐng)域?qū)?。?yīng)用層:負(fù)責(zé)應(yīng)用中UI屏幕之間的導(dǎo)航,以及與其它系 統(tǒng)應(yīng)用層之間的交互。還能對(duì)用戶(hù)輸入的數(shù)據(jù)進(jìn)行基本(非 業(yè)務(wù)相關(guān))的驗(yàn)證,然后再把數(shù)據(jù) 傳到應(yīng)用的其 它層(更底層)。 不包含任何 業(yè)務(wù)、領(lǐng)域相關(guān)的邏輯、或數(shù)據(jù) 訪(fǎng)問(wèn)邏輯。 沒(méi)有任何反映

14、商 業(yè)用例的狀 態(tài),但卻能 處理用戶(hù)會(huì)話(huà)或任務(wù)進(jìn)展的狀態(tài)。領(lǐng)域?qū)樱贺?fù)責(zé)業(yè)務(wù)領(lǐng) 域的概念,業(yè)務(wù)用例和業(yè)務(wù)規(guī)則的相關(guān)信息。領(lǐng)域?qū)ο蠓庋b了業(yè)務(wù)實(shí)體的 狀態(tài)和行為。貸款處理應(yīng)用中的業(yè)務(wù)實(shí)體例子有抵押(Mortgage )、房產(chǎn)(Property ) 和貸款人(Borrower )。如果用例跨越多個(gè)用 戶(hù)請(qǐng)求(比如貸款登記過(guò)程包含多個(gè) 步驟:用戶(hù)輸入貸款詳細(xì)信 息,系統(tǒng)基于貸款特性返回產(chǎn)品和利率,用戶(hù)選擇特定的產(chǎn)品/利率組合,最后系統(tǒng)會(huì) 用這個(gè)利率鎖定貸款),還可以管理業(yè)務(wù)用例的狀態(tài)(會(huì)話(huà))。包含服務(wù)對(duì)象,這些服務(wù)對(duì)象只包含一個(gè)定 義好的、不屬于任何 領(lǐng)域?qū)ο蟮目刹僮餍?為。服務(wù)封裝了業(yè)務(wù)領(lǐng)域的狀態(tài),而

15、業(yè)務(wù)領(lǐng)域并不適用于 領(lǐng)域?qū)ο蟊旧?。是商業(yè)應(yīng)用的核心,應(yīng)該與應(yīng)用的其它層隔離開(kāi)來(lái)。而且,它不 應(yīng)該依賴(lài)于其它層使 用的應(yīng)用框架(JSP/JSF、Struts、EJB、Hibernate 、XMLBeans 等)。卜面的圖2顯示了應(yīng)用中使用的不同架極 層次,以及它們與DDD有怎樣的關(guān)系loan Applicaticn ArhiUrturc Diaq ram圖2.多層應(yīng)用架極圖。燦查看大圖)下面的設(shè)計(jì)觀(guān)點(diǎn)被認(rèn)為是目前DDD實(shí)現(xiàn)訣竅的主要部分: 面向?qū)ο缶幊蹋∣OP) 依賴(lài)注入(DL) 面向方面編程(AOP )OOP是領(lǐng)域?qū)崿F(xiàn)中最重要的基本原貝U。應(yīng)該利用像繼承、封裝和多態(tài)這樣的OOP 概念,使用Pl

16、ain Java類(lèi)和接口來(lái)設(shè)計(jì)領(lǐng)域?qū)ο?。大部分領(lǐng)域元素是既有狀 態(tài)(屆 性)乂有行為(操作腿的方法或操作)的真正對(duì)象。它們同時(shí)對(duì)應(yīng)丁真實(shí)世界的概 念,能很合 適地適用丁 OOP概念。DDD中的實(shí)體和值對(duì)象都是OOP概念的典型 例子,因?yàn)樗鼈兺瑫r(shí)有狀態(tài)和行為。在典型的工作單元UOW中,領(lǐng)域?qū)ο笮枰c其它的對(duì)象協(xié)作,無(wú)論這些對(duì)象是服 務(wù)、資源庫(kù)、還是工廠(chǎng)。領(lǐng)域?qū)ο筮€需要處理其它那些本身就橫切的 關(guān) 注點(diǎn),比 如領(lǐng)域狀態(tài)變化跟蹤、審計(jì)、緩存、事務(wù)管理(包括事務(wù)重試)。這些都是可重用、非 領(lǐng)域相關(guān)的關(guān)注點(diǎn),通常很容易在包括 領(lǐng)域?qū)拥恼麄€(gè)代碼中散布和重復(fù)。在領(lǐng)域 對(duì)象中嵌入該邏輯會(huì)導(dǎo)致領(lǐng)域?qū)雍头穷I(lǐng)域相關(guān)

17、的代碼互相糾纏、產(chǎn)生混亂。說(shuō)到處理對(duì)象問(wèn)之沒(méi)有緊耦合的代碼依賴(lài)關(guān)系和隔離橫切關(guān)注點(diǎn)的時(shí)候,OOP并 不能獨(dú)自為領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和開(kāi)發(fā)提供條好的設(shè)計(jì)解決方案。在這是可以利用DI和 AOP這樣的設(shè)計(jì)概念對(duì)OOP進(jìn)行補(bǔ)充,以盡量減少緊耦合、提高模塊化、更好地處 理橫切關(guān)注點(diǎn)。依賴(lài)注入DI能很有效地將配置和依 賴(lài)代碼從領(lǐng)域?qū)ο笾幸瞥?。此外,領(lǐng)域類(lèi)對(duì)數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象 DAO)類(lèi)、月嬌類(lèi)對(duì)領(lǐng)域類(lèi)的設(shè)計(jì)依賴(lài)性使得DI成為DDD實(shí)現(xiàn)中“瀕有”的內(nèi) 容。顧i將資源庫(kù)和服務(wù)之類(lèi)的其它對(duì)象注入到領(lǐng)域?qū)ο?,di有助丁創(chuàng)建一個(gè)更活 晰、松耦合的設(shè)計(jì)。在示例應(yīng)用中,服務(wù)對(duì)象FundingServiceImpD利用DI注入實(shí)體對(duì)象

18、Loan、 Borrower和FundingRequesD。實(shí)體也通過(guò)DI引用資源庫(kù)。IW羊的,像數(shù)據(jù)源、 Hibernate會(huì)話(huà)工廠(chǎng)和事務(wù)管理器這些其它的Java EE資源也被注入到服 務(wù)和資源 庫(kù)對(duì)象中。面向方面編程通過(guò)從領(lǐng)域?qū)ο笾幸瞥龣M切關(guān)注點(diǎn)代碼,比如檢查、領(lǐng)域狀態(tài)變化跟蹤等,AOP有 助丁實(shí)現(xiàn)一個(gè)更好的設(shè)計(jì)(即右領(lǐng)域模型中少一些亂七八糟的內(nèi)容)。可 利用 AOP把協(xié)同對(duì)象和服務(wù)注入領(lǐng)域?qū)ο螅綰是那些容器沒(méi)有 實(shí)例化的對(duì)象(比如持 久化對(duì)象)。在可以利用AOP的領(lǐng)域?qū)又?,其它的方面有緩存、事?wù)管理和基 丁角 色的安全(授權(quán))。貸款處理應(yīng)用利用自定義方面將數(shù)據(jù)緩存引入服務(wù)對(duì)象。貸款產(chǎn)品

19、和利率信息從 數(shù)據(jù)庫(kù)表中加載一次(名戶(hù)端第一次請(qǐng)求這些信息時(shí)),然后御到適用丁后面產(chǎn) 品和利率查找的對(duì)象緩存JBossCache)中。產(chǎn)品和利率會(huì)被頻繁訪(fǎng)問(wèn)、但不會(huì)定 期更新,所以緩存數(shù)據(jù)是一個(gè)很好的候 選方案,而不是每次都從后端的數(shù)據(jù) 庫(kù)獲 取。在近期的討論貼子里,DDD中DI和AOP概念的作用是主要的 話(huà)題。討論以 Ramnivas Laddad的演講為基礎(chǔ),Ramnivas在其演講中主張,沒(méi)有AOP和DI的幫 助,DDD無(wú)法實(shí)現(xiàn)。Ramnivas在這個(gè)演講中討論了細(xì)粒度DI”的概念,這一概念 利用AOP使領(lǐng)域?qū)ο蠡謴?fù)機(jī)敏性。他說(shuō)領(lǐng)域?qū)ο笮枰L(fǎng)問(wèn)其它細(xì)粒度的對(duì)象來(lái)提 供豐富的 行為,該問(wèn)題

20、的解決方案是在領(lǐng)域?qū)ο笾凶⑷敕?wù)、工廠(chǎng)或資源庫(kù)(通寸 在調(diào)用極造或setter方法時(shí)期使用方面來(lái)注入依 賴(lài))。Chris Richardson也討論了有關(guān)利用DI、對(duì)象和方面,通減少耦合、提高模塊化來(lái) 改進(jìn)應(yīng)用設(shè)計(jì)。Chris談到了 “嶇大服務(wù)”反模式,這是應(yīng)用代碼耦合、混亂、分散 的結(jié)果,他還談了如何利用DI和AOP的概念來(lái)避免這一反模式。注解最近定義、處理方面和DI的趨勢(shì)是使用注解。對(duì)實(shí)現(xiàn)遠(yuǎn)程服務(wù)(比如EJB或Web Services)初,注解有助丁減少所需的工件。它們還簡(jiǎn)化了配置管理佚務(wù)。Spring 2.5、Hibernate 3,以及其它框架都充分利用注解在Java企業(yè)應(yīng)用的不同層中

21、配置 組件。我們應(yīng)該利用注解生成模板代碼,模板代碼能在靈活性上增加價(jià)值。但同時(shí)應(yīng)該謹(jǐn) 慎使用注解。注解應(yīng)該用丁不會(huì)引起混淆或 誤解實(shí)際代碼的地方。使用注解 的一 個(gè) 很好的例子是Hibernate ORM映射,注解能直接用類(lèi)或?qū)眯悦o指定的SQL表 或列名添加值。另一方面,像JDBC驅(qū)動(dòng)配置驅(qū)動(dòng)類(lèi)名、JDBC URL、用戶(hù)名和密 碼)這樣的詳細(xì)信息則更適合丁存放在XML文件中,而不是使用注解。這基丁數(shù)據(jù) 庫(kù)在同一個(gè)上下文中 這一假設(shè)。如頃域模型和數(shù)據(jù)庫(kù)表之 間需要相當(dāng)多的轉(zhuǎn) 換,那就應(yīng)該好好思考一下設(shè)計(jì)了。Java EE 5 提供 JPA 注解,t匕如£示四、PersistenceU

22、nit PersistenceContext 等 以此給簡(jiǎn)單的Java類(lèi)添加持久化細(xì)節(jié)。仲域建模上下文中,實(shí)體、資源庫(kù)和服務(wù)' 都是使用注解的好地方。©Configurable Spring將資源庫(kù)和服務(wù)注入領(lǐng)域?qū)ο蟮姆绞健pring框架在 Configurable注解之上擴(kuò)展了 領(lǐng)域?qū)ο笠蕾?lài)注入”思想。Ramnivas最近在博客中 談?wù)摿思磳l(fā)布的Spring 2.5.2版本(從項(xiàng)目的Snapshot Build 379開(kāi)始可用)的R新 改進(jìn)。有三個(gè)新的方面(AnnotationBeanConfigurerAspect、AbstractInterfaceDrivenDep

23、endencyInjectionAspect 木日AbstractDependencyInjectionAspect)為領(lǐng)域?qū)ο笠蕾?lài)注入提供了簡(jiǎn)單、更靈活的選 擇。Ramnivas說(shuō),弓I入中間的方 面AbstractInterfaceDrivenDependencyInjectionAspect ),其主要原因是領(lǐng)域特定的注解和接口發(fā)揮 作用。Spring還提供了其它注解來(lái)幫助 設(shè)計(jì)領(lǐng)域?qū)ο?,比?©Repository Service和 Transactional示例應(yīng)用中使用了部分注解。實(shí)體對(duì)象Loan> Borrower和FundingRequest使用了 Entity

24、注解;這些對(duì)象還使用©Configurable注解綁定資源庫(kù)對(duì)象;月盼類(lèi)也使用 Transactional注解來(lái)用事務(wù)行為裝飾服務(wù)方法。領(lǐng)域模型和安全領(lǐng)域?qū)拥膽?yīng)用安全確保只有授 權(quán)的客戶(hù)端(/類(lèi)用戶(hù)或其它應(yīng)用)育朗用領(lǐng)域操 作,訪(fǎng)問(wèn)領(lǐng)域狀態(tài)。Spring安全Spring Portfolio的一個(gè)子項(xiàng)目)同寸為應(yīng)用的表現(xiàn)層(以URL為基礎(chǔ)) 和領(lǐng)域?qū)樱ǚ教峁┡捞锪6鹊脑L(fǎng)問(wèn)控制。該框架使用Spring的Bean Proxy來(lái) 攔截方法調(diào)用,運(yùn)用安全約束。它為使用MethodSecurityInterceptor類(lèi)的Java對(duì)象 提供了基丁角色的聲明式安全。它也有 針對(duì)領(lǐng)域?qū)ο蟮脑L(fǎng)問(wèn)控制列

25、表(ACL's)形式 的實(shí)例級(jí)別安全,以控制實(shí)例級(jí)別的用戶(hù)訪(fǎng)問(wèn)。在領(lǐng)域模型中使用Spring安全來(lái)處理授權(quán)需求的主要好處是,框架有一個(gè)非侵入 式的架極,我們可以完全隔離 領(lǐng)域和安全方面。此外,業(yè)務(wù)對(duì)象也不會(huì)和安全 實(shí)現(xiàn) 細(xì)節(jié)混成一團(tuán)。我門(mén)可以只在一個(gè)地方 編寫(xiě)通用的安全 規(guī)則,(使MOP技術(shù))在 佚何需要實(shí)現(xiàn)它們的地方運(yùn)用它們。在領(lǐng)域和服務(wù)類(lèi)中,謎在類(lèi)方法調(diào)用級(jí)別進(jìn)行處理。舉例來(lái)說(shuō),對(duì)丁高達(dá)一白萬(wàn) 美元的貸款,承保領(lǐng)域?qū)ο笾械馁J款審批”方法可以由佚何具有“承保人”角色 的用戶(hù)調(diào)用;頃丁超過(guò)一白萬(wàn)美元的貸款申請(qǐng)來(lái)說(shuō),同ft域?qū)ο笾械膶徟椒?則只能由具有“核保主管”角色的用戶(hù)調(diào)用。下表簡(jiǎn)

26、要說(shuō)明了應(yīng)用架極每一層中應(yīng)用的各種安全關(guān)注點(diǎn) 表1.各個(gè)應(yīng)用層中的安全關(guān)注點(diǎn)層安全關(guān)注點(diǎn)客戶(hù)端/控制器認(rèn)證、Web頁(yè)面(URL )界別授權(quán)外觀(guān)基于角色的授權(quán)領(lǐng)域領(lǐng)域?qū)嵗?jí)別授權(quán)、ACL數(shù)據(jù)庫(kù)DB對(duì)象級(jí)別授權(quán)(存儲(chǔ)過(guò)程、存儲(chǔ)函數(shù)、觸 發(fā)器)業(yè)務(wù)規(guī)則業(yè)務(wù)規(guī)則是業(yè)務(wù)領(lǐng)域中的重要部分。它們定義了數(shù)據(jù)驗(yàn)證和其它的約束規(guī)則,這些規(guī)則需要應(yīng)用丁特定業(yè)務(wù)流程場(chǎng)景中的領(lǐng)域?qū)ο蟆I(yè)務(wù)規(guī)則通常分為下面幾類(lèi):數(shù)據(jù)驗(yàn)證數(shù)據(jù)轉(zhuǎn)換商業(yè)決策流程流向(工作流邏輯)上下文在DDD世界中非常重要。上下文的特性決定了 領(lǐng)域?qū)ο髤f(xié)作及其它運(yùn)行時(shí) 因素,比如運(yùn)用什么業(yè)務(wù)規(guī)則等。驗(yàn)證以及其它業(yè)務(wù)規(guī)則往往都是在一個(gè)特 定的 業(yè) 務(wù)上下文中處

27、理的。這意味著,相同的領(lǐng)域?qū)ο笤诓煌臉I(yè)務(wù)上下文中將不得 不處理不同的一組業(yè)務(wù)規(guī)則。比如說(shuō),逾i 了貸款審批流程中的承保 步驟后,貸款 領(lǐng)域 對(duì)象的一些屆性(像貸款數(shù)額和利率)就不能再改變了。但在貸款剛剛登記并 與特定利率關(guān)聯(lián)的時(shí)候,翩羊的屆性是可以改 變的。盡管所有的領(lǐng)域特 定業(yè)務(wù)規(guī)則都應(yīng)該封裝在領(lǐng)域?qū)?,但一些?yīng)用設(shè)計(jì)將規(guī)則放在 了外觀(guān)類(lèi)中,這導(dǎo)致了領(lǐng)域類(lèi)在業(yè)務(wù)規(guī)則邏輯方面變成了 貧血的”。在小本應(yīng)用中 這可能是可接受的 解決方案,但不推薦將其用丁包含 復(fù)雜業(yè)務(wù)規(guī)則的中大型企業(yè) 應(yīng)用。更好的設(shè)計(jì)方案是把規(guī)則放在它們應(yīng)該在的地方一一 域?qū)ο笾?。如果一個(gè) 業(yè)務(wù)規(guī)則跨越兩個(gè)或兩個(gè)以上的實(shí)體對(duì)象,丹

28、隊(duì)該規(guī)則應(yīng)該做為服務(wù)類(lèi)的一部此外,如果我們不在應(yīng)用中下苦功,往往把業(yè)務(wù)規(guī)則變成代碼里的一申switch語(yǔ) 句。隨著規(guī)則變得越來(lái)越復(fù)雜,開(kāi)發(fā)人員不會(huì)愿意花費(fèi)時(shí)間去重極代 碼,將 switch語(yǔ)句移到更易丁管理的 設(shè)計(jì)中。在類(lèi)中硬編碼復(fù)雜的流向或決策規(guī)則邏輯 會(huì)導(dǎo)致類(lèi)中出現(xiàn)更長(zhǎng)的方法、代碼重復(fù)、蟀僵化的應(yīng)用設(shè)計(jì),長(zhǎng)遠(yuǎn)來(lái)看,這 將成 為維護(hù)的噩夢(mèng)。一個(gè)良好的設(shè)計(jì)是把所有的規(guī)則(鎖U是隨著業(yè)務(wù)策略的變化而頻 繁改變的復(fù)雜規(guī)則)放至規(guī)則引擎(利用規(guī)則框架,比如JBoss Rules、OpenRules或 Mandarax)中去、并順域類(lèi)中進(jìn)行調(diào)用。驗(yàn)證規(guī)則通常會(huì)用不同的 語(yǔ)言實(shí)現(xiàn),比如Javascrip

29、t、XML、Java代碼,還有其它腳 本語(yǔ)言。但由丁業(yè)務(wù)規(guī)則的動(dòng)態(tài)特性,Ruby、Groovy、領(lǐng)域特定語(yǔ)言DSL)這些腳 本語(yǔ)言是定義、管些規(guī)則更好的選擇。Struts應(yīng)用層)Spring (月路層)和 Hibernate ORM)都有其自己的驗(yàn)證模塊,我們可以在這些驗(yàn)證模塊中對(duì)傳入或傳 出的數(shù)據(jù)對(duì)象運(yùn)用驗(yàn)證規(guī)則。在一些情況下,驗(yàn)證規(guī)則還能被處理為方面,它們可 以組合到應(yīng)用的不同層次中去(比如服務(wù)和控 制器)。在編寫(xiě)領(lǐng)域類(lèi)處理業(yè)務(wù)規(guī)則時(shí),緊記單元測(cè)試方面是非常重要的。規(guī)則邏輯中的佚 何變化都應(yīng)該很容易、獨(dú)立地單元可測(cè)。示例應(yīng)用包括一個(gè)業(yè)務(wù)規(guī)則集來(lái)驗(yàn)證貸款特性是否都在允 許的產(chǎn)品和利率規(guī)格內(nèi)。

30、規(guī)則在腳本語(yǔ)言中Groovy)進(jìn)行定義,并用于傳遞給FundingService對(duì)象的貸 款數(shù)據(jù)。設(shè)計(jì)從設(shè)計(jì)的角度出發(fā),領(lǐng)域?qū)討?yīng)該有一個(gè)定義活晰的邊界,以避免來(lái)自非核心領(lǐng)域?qū)?關(guān)注點(diǎn)的層的損壞,比如特定供應(yīng)商的說(shuō)明、數(shù)據(jù)過(guò)濾、轉(zhuǎn)換等。領(lǐng)域元素 應(yīng)該設(shè) 計(jì)為正確地保存領(lǐng)域狀態(tài)和行為。不同的領(lǐng)域元素會(huì)基丁狀 態(tài)和行為進(jìn)行不同的 結(jié)極化。下面的表2展示了領(lǐng)域元素及其包含的內(nèi)容。表2.領(lǐng)域元素及其狀態(tài)和行為領(lǐng)域元素狀態(tài)/行為實(shí)體、值對(duì)象、聚合狀態(tài)和行為都有數(shù)據(jù)傳輸對(duì)象只有狀態(tài)服務(wù)、資源庫(kù)只有行為同時(shí)包含狀態(tài)(數(shù)據(jù))和彳為(操作)性體、值對(duì)象、聚合應(yīng)該有定義活晰的狀態(tài)和 行為。同時(shí),該行為不應(yīng)該超出對(duì)象

31、邊界的范圍。實(shí)體應(yīng)該在作用丁本地狀態(tài)的用 例中完成大部分工作。但它們不應(yīng)該知道太多無(wú)關(guān)的概念。對(duì)那些封裝領(lǐng)域?qū)ο鬆顟B(tài)所需要的屆性來(lái) 說(shuō),好的設(shè)計(jì)實(shí)踐是只包括這些屆性的 getter/setter方法。設(shè)計(jì)領(lǐng)域?qū)ο髸r(shí),只為那些能改變的屆性提供setter方法。此 外,公有的極造函數(shù)應(yīng)該只含有必需的屆性,而不是包含 領(lǐng)域類(lèi)中所有的屆性。在大部分用例中,我們并不是真的要去直接改 變對(duì)象的狀態(tài)。所以,代替改變內(nèi)部 狀態(tài)的做法是,創(chuàng)建一個(gè)帶有已改變狀態(tài)的新對(duì)象并返回該新對(duì)象。這種方法在這 些用例中就足 夠了,還能降低設(shè)計(jì)的復(fù)雜性。聚合類(lèi)對(duì)調(diào)用者隱藏了協(xié)作類(lèi)的用法。聚合類(lèi)可用來(lái)封裝領(lǐng)域類(lèi)中復(fù)雜的、有侵入

32、性的、狀態(tài)依賴(lài)的需求。支持DDD的設(shè)計(jì)模式有幾種有助丁領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)和開(kāi)發(fā)的設(shè)計(jì)模式。下面是這些設(shè)計(jì)模式的列表: 領(lǐng)域?qū)ο螅―O )數(shù)據(jù)傳輸對(duì)象(DTO ) DTO組裝器資源庫(kù):資源庫(kù)包含領(lǐng)域?yàn)橹行牡姆椒?,并使?DAO與數(shù)據(jù)庫(kù)交互。 泛型DAO 時(shí)態(tài)模式(Temporal Patterns):這些模式給豐富的領(lǐng)域模型添加了 時(shí)間維。Bitemporal框架基于Martin Fowler的時(shí)態(tài)模式,為處理領(lǐng)域模型中的雙 時(shí)態(tài)問(wèn)題 提供了設(shè)計(jì)方法。核心的 領(lǐng)域?qū)ο蠹捌潆p 時(shí)態(tài)屬性能用ORM產(chǎn)品持久化,比如 Hibernate 。在DDD中應(yīng)用的其它設(shè)計(jì)模式還包括策略模式、外觀(guān)模式和工廠(chǎng)模式。Jim

33、my Nilsson在他的E里討論了工廠(chǎng)模式,認(rèn)為它是一種領(lǐng)域模式。DDD反模式在最佳實(shí)踐和設(shè)計(jì)模式的反面,架極帥和開(kāi)發(fā)人員在實(shí)現(xiàn)領(lǐng)域模型時(shí)還應(yīng)該提防 一些DDD的壞氣味。由丁這些反模式,領(lǐng)域?qū)釉趹?yīng)用架極中成為最不重要的部分, 外觀(guān)類(lèi)反而在模型中承擔(dān)了更重要的 責(zé)佚。下面是一些反模式: 貧血的領(lǐng)域?qū)ο?重復(fù)的DAO肥服務(wù)層:服務(wù)類(lèi)在這里最終會(huì)包含所有的 業(yè)務(wù)邏輯。 依戀情結(jié)(Feature Envy):這是Martin Fowler 在他關(guān)于重構(gòu)的 空中提到的典型的壞 氣味,在該反模式中,一個(gè) 類(lèi)的方法對(duì)屬于其它類(lèi)的數(shù)據(jù)太 過(guò)念念不忘。數(shù)據(jù)訪(fǎng)問(wèn)對(duì)象DAO和資源庫(kù)在領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)中都很重要。DAO

34、是關(guān)系型數(shù)據(jù)庫(kù)和應(yīng)用之間的契 約。它封裝了 Web應(yīng)用中的數(shù)據(jù)庫(kù)CRUD操作細(xì)節(jié)。另一方面,資源庫(kù)是一個(gè)獨(dú)立 的抽象,它與DAO進(jìn)行交互,并提供到領(lǐng)域模型的“業(yè)務(wù)接口” 。資源庫(kù)使用領(lǐng)域的通用語(yǔ)言,處理所有必要的DAO,并使用領(lǐng)域理解的語(yǔ)言提供 對(duì)領(lǐng)域模型的數(shù)據(jù)訪(fǎng)問(wèn)服務(wù)。DAO方法是細(xì)粒度的,更接近數(shù)據(jù)庫(kù),質(zhì)源庫(kù)方法的粒度粗一些,而且更接近領(lǐng) 域。此外,一個(gè)資源庫(kù)類(lèi)中能注入多個(gè)DAO。資源庫(kù)和DAO能防止解耦的領(lǐng)域模型 去處理數(shù)據(jù)訪(fǎng)問(wèn)和持久化細(xì)節(jié)。領(lǐng)域?qū)ο髴?yīng)該只依賴(lài)丁資源庫(kù)接口。這就是為什么是注入資源庫(kù)、而不是DAO會(huì) 產(chǎn)生一個(gè)更規(guī)則的領(lǐng)域模型的原因。DAO類(lèi)不能由客戶(hù)端(月盼和其它的消費(fèi)者

35、類(lèi))直搠I用。名戶(hù)端應(yīng)該始終調(diào)用領(lǐng)域?qū)ο?,領(lǐng)域?qū)ο笤僬{(diào)用DAO將數(shù)據(jù)持久化 到數(shù)據(jù)存儲(chǔ)中。處理領(lǐng)域?qū)ο笾g的依賴(lài)關(guān)系(比女映體及其資源庫(kù)之間的依賴(lài)關(guān)系)虎開(kāi)發(fā)人員 經(jīng)常遇到的典型問(wèn)題。解決這個(gè)問(wèn)題通常的設(shè)計(jì)方案是讓服務(wù)類(lèi)或外觀(guān)類(lèi)直 接調(diào) 用 資源庫(kù),仙用資源庫(kù)的時(shí)候返回實(shí)體對(duì)象給客戶(hù)端。該設(shè)計(jì)最終導(dǎo)致前面提 到的貧血領(lǐng)域模型,其中外觀(guān)類(lèi)會(huì)開(kāi)始堆積更多的業(yè)務(wù)邏輯,而領(lǐng)域?qū)ο髣t成為單 純的 數(shù)據(jù)載體。好的設(shè)計(jì)是利用DI和AOP技術(shù)將資源庫(kù)和服務(wù)注入到領(lǐng)域?qū)ο?中去。示例應(yīng)用在實(shí)現(xiàn)貸款處理領(lǐng)域模型時(shí)遵循了這些設(shè)計(jì)原則。持久化持久化是一個(gè)基 礎(chǔ)設(shè)施方面,領(lǐng)域?qū)討?yīng)該與其解耦。JPA通過(guò)對(duì)類(lèi)隱藏持久化實(shí)現(xiàn)

36、 的細(xì)節(jié),提供了這一抽象。它由注解推動(dòng),所以不需要XML映射文件。但同時(shí),表 名和列名嵌在代碼中,在某些情況下可能并不是一個(gè)靈活的解決辦法。使用提供數(shù)據(jù)網(wǎng)格解決方案的網(wǎng)格 計(jì)算產(chǎn)品,比如Oracle的Coherence、 WebSphere的Object Grid、GigaSpaces,開(kāi)發(fā)人員在建模和設(shè)計(jì)業(yè)務(wù)領(lǐng) 域時(shí),完全 不需要考慮RDBMS。數(shù)據(jù)庫(kù)層用內(nèi)存對(duì)象/數(shù)據(jù)網(wǎng)格的形式從 領(lǐng)域?qū)映橄蟪鰜?lái)。緩存在我們討論領(lǐng)域?qū)拥臓顟B(tài)(數(shù)據(jù)歸寸,珂門(mén)不得不談到緩存問(wèn)題。經(jīng)常訪(fǎng)問(wèn)的領(lǐng)域數(shù) 據(jù)(比如抵押貸款處理應(yīng)用中的產(chǎn)品和利率)彳艮值得緩存起來(lái)。緩存能提高性能,減 少數(shù)據(jù)庫(kù)服務(wù)器的負(fù)載。月嬌層很適合緩存

37、領(lǐng)域狀態(tài)。TopLink和Hibernate這些 ORM框架也提供數(shù)據(jù)緩存。貸款處理示例應(yīng)用使用JBossCache框架來(lái)緩存產(chǎn)品和利率詳情,以減少數(shù)據(jù)庫(kù)調(diào) 用、提高應(yīng)用性能。事務(wù)管理對(duì)保持?jǐn)?shù)據(jù)完整性、整體提交或回 滾UOW(工竹單元模式)加,事務(wù)管理是很重 要的。應(yīng)該在應(yīng)用架極層的哪里處理事務(wù)一直存在爭(zhēng)議。交叉實(shí)體的事務(wù)(在同一 UOW中跨越多個(gè)領(lǐng)域?qū)ο螅┮灿绊懺谀睦锾幚硎聞?wù)這一設(shè)計(jì)決策。一些開(kāi)發(fā)人員傾向丁在DAO類(lèi)中管理事務(wù),這是一個(gè)欠佳的設(shè)計(jì)。該設(shè)計(jì)導(dǎo)致過(guò) 細(xì)粒度的事務(wù)控制,對(duì)那些事務(wù)跨越多個(gè)領(lǐng)域?qū)ο蟮挠美齺?lái)說(shuō),這種事務(wù)控 制沒(méi) 有 靈活性。服務(wù)類(lèi)應(yīng)該處理事務(wù);即使事務(wù)跨越多個(gè)領(lǐng)域?qū)ο螅?/p>

38、服務(wù)類(lèi)也能處理事 務(wù),電在大多數(shù)用例中,是服務(wù)類(lèi)在處理控制流。示例應(yīng)用中的FundingServiceImpl類(lèi)處理資金申請(qǐng)的事務(wù),過(guò)調(diào)用資源庫(kù)執(zhí)行多 個(gè)數(shù)據(jù)庫(kù)操作,并在單一事務(wù)中提交或回滾所有的數(shù)據(jù)庫(kù)變化。數(shù)據(jù)傳輸對(duì)象領(lǐng)域?qū)ο竽P驮诮Y(jié)極上與從業(yè)務(wù)服務(wù)接收或發(fā)送的消息不茲容,在這樣一種SOA 環(huán)境中,DTO就是設(shè)計(jì)中很重要的一部分。消息通常都在XML模式定義 文檔XSD)中叔和維護(hù),從XSD編寫(xiě)(或伽生成)DTO對(duì)象,并在領(lǐng)域和SOA服務(wù) 層之間使用它們來(lái)傳輸數(shù)據(jù)(消息)是坤t1普遍的做法。在分布式應(yīng)用 中,將來(lái)自 丁一個(gè)或多個(gè)領(lǐng)域?qū)ο笾械臄?shù)據(jù)映射到DTO中會(huì)成為必然的弊端,因?yàn)閺男阅芎?安全

39、角度出發(fā),跨越網(wǎng)絡(luò)發(fā)送領(lǐng)域?qū)ο笫遣粚?shí)際的。從DDD的角度來(lái)看,DTO還有利丁維護(hù)服務(wù)層和UI層之間的縫隙,其中DO用丁 領(lǐng)域?qū)雍头?wù)層,DTO用丁表現(xiàn)層。Dozer框架用7將一或多個(gè) 領(lǐng)域?qū)ο蠼M裝為一個(gè)DTO對(duì)象。它是雙向的,將領(lǐng)域?qū)?象轉(zhuǎn)換為DTO的時(shí)候,它會(huì)保存大量備用的代碼和時(shí)限,反之亦然。DO和DTO之 間的雙向映射有利丁消除“ DCEU DTO和“DTCEU DO各自的轉(zhuǎn)換邏輯。該框架還 能正確處理類(lèi)型和數(shù)組的轉(zhuǎn)換。示例應(yīng)用在資金處理申請(qǐng)到來(lái)時(shí),利用Dozer映射文件(XML)將FundingRequestDTO對(duì)象劃分成為 Loan、Borrower、FundingRequest實(shí)

40、體對(duì)象。在 返回給客戶(hù)端時(shí),映射同樣負(fù)責(zé)將來(lái)自實(shí)體的資金響應(yīng)數(shù)據(jù)聚合到單一的DTO對(duì) 象中。DDD實(shí)現(xiàn)框架像 Spring、Real Object Oriented ROO) Hibernate 和 Dozer 這些框架都有助丁 設(shè)計(jì) 并實(shí)現(xiàn)領(lǐng)域模型。支持DDD實(shí)現(xiàn)的其它框架有 Naked Objects、Ruby On Rails、 Grails,以及 Spring Modules XT Framework。SPig負(fù)責(zé)實(shí)例化,并將服務(wù)、工廠(chǎng)和資源庫(kù)這些領(lǐng)域類(lèi)聯(lián)接在一起。它還使用 ©Configurable解將服務(wù)注入實(shí)體。該注解是Spring特有的,所以完成這一注入的 其它選擇是

41、使用諸如Hibernate攔截器的東西。ROO是建立在觀(guān)點(diǎn) 領(lǐng)域第一,基礎(chǔ)設(shè)施第二”之上的DDD實(shí)現(xiàn)框架。開(kāi)發(fā)該框架 是為了減少Web應(yīng)用開(kāi)發(fā)中模式的模板編碼。利用ROO時(shí),我門(mén)定義領(lǐng)域模型, 接著框架(基丁 Maven Archetype動(dòng)為模型-視圖-控制器MVC)、DTO、業(yè)務(wù)層外觀(guān) 和DAO層生成代碼。它也能為單元測(cè)試和集成測(cè)試生成stubs。ROO有幾個(gè)非常實(shí)用的實(shí)現(xiàn)模式。比如說(shuō),它區(qū)分處理屆性的狀態(tài)、使用屆性級(jí)訪(fǎng) 問(wèn)的持久層、只反映必需屆性的公有極造函數(shù)。開(kāi)發(fā)沒(méi)有實(shí)際的實(shí)現(xiàn),模型就沒(méi)有用處。實(shí)現(xiàn)階段應(yīng)該盡可能多地自動(dòng)化完成開(kāi)發(fā)佚 務(wù)。為了看看什么佚務(wù)能自動(dòng)完成,讓我們看看涉及領(lǐng)域模

42、型的一個(gè)典型用例。下 面是用例的步驟列表:輸入請(qǐng)求: 客戶(hù)端調(diào)用外觀(guān)類(lèi),以XML文檔(XSD茲容的)的方式 發(fā)送數(shù)據(jù);外 觀(guān)類(lèi)為UOW 初始化一個(gè)新的事務(wù)。 驗(yàn)證輸入的數(shù)據(jù)。驗(yàn)證包括基本 驗(yàn)證(基本的/數(shù)據(jù)類(lèi)型/屬性級(jí)檢查)和業(yè)務(wù)驗(yàn)證。如 果有任何的 驗(yàn)證錯(cuò)誤,拋出適當(dāng)?shù)漠惓?。將描述轉(zhuǎn)換為代碼(以成為簡(jiǎn)單的領(lǐng)域)。改變數(shù)據(jù)格式,以成 為簡(jiǎn)單的領(lǐng)域模型。進(jìn)行所有的屬性分割(比如,在客 戶(hù)實(shí)體對(duì)象中,將客戶(hù)姓名分成名字和姓)。 把DTO拆分為一或多個(gè) 領(lǐng)域?qū)ο?。持久化領(lǐng)域?qū)ο蟮臓顟B(tài)。輸出響應(yīng):從數(shù)據(jù)存儲(chǔ)中獲取領(lǐng)域?qū)ο蟮臓顟B(tài)。如果必要,緩存狀態(tài)。將領(lǐng)域?qū)ο蠼M裝為對(duì)應(yīng)用有利的數(shù)據(jù) 對(duì)象(DTO )。進(jìn)

43、行所有的數(shù)據(jù)元素合并或分離(比如結(jié)合名字和姓,組成單一的客戶(hù)姓名屬性)。將代碼轉(zhuǎn)換為描述。必要時(shí)改變數(shù)據(jù)格式,以 處理客戶(hù)端數(shù)據(jù)使用的要求。如果有必要,緩存DTO的狀態(tài)。事務(wù)提交(如果有 錯(cuò)誤則回滾),退出控制流。下表顯示了應(yīng)用中不同的對(duì)象,這些對(duì)象將一個(gè)層的數(shù)據(jù)傳到另一個(gè)層。 表3.應(yīng)用層間的數(shù)據(jù)流向1 層起點(diǎn)對(duì)象終點(diǎn)對(duì)象框架DAO數(shù)據(jù)庫(kù)表DOHibernate領(lǐng)域委托DODTODozer數(shù)據(jù)傳輸DTOXMLJAXB正如你所看到的,相同的數(shù)據(jù)以不同形式(DO、DTO、XML等)仙用架極中傳遞的 層并不多。大部分持有數(shù)據(jù)的這些對(duì)象Java或XML)還 有像DAO、DAOImpl、 DAOTes

44、t這些類(lèi)實(shí)際上都是基礎(chǔ)設(shè)施。這些有模板代碼和結(jié)極的類(lèi)、XML文件都很 適合代碼生成。代碼生成ROO這樣的框架還為新項(xiàng)目創(chuàng)建了一個(gè)標(biāo)準(zhǔn)、一致的項(xiàng)目模板(使用Maven插 件)。使順先生成的項(xiàng)目模板,我們可以實(shí)現(xiàn)目錄結(jié)極的一致性,其中存放源碼、 測(cè)試類(lèi)、配置文件,以及對(duì)內(nèi)部和外部(第三方)組件庫(kù)的依賴(lài)關(guān)系。典型的企業(yè)軟件應(yīng)用所需的種種類(lèi)和配置文件時(shí),其數(shù)量之多令人望而生畏。代碼 生成是解決該問(wèn)題的最好辦法。代碼生成工具通常使用某 類(lèi)模板框架來(lái)定義模板, 或是代碼生成器能從中生成代 碼的映射。Eclipse建??蚣蹺MD的幾個(gè)子項(xiàng)目有 助丁 Web應(yīng)用項(xiàng)目需要的各種工件的代碼生成。模型驅(qū)動(dòng)架極MDA

45、)工具,比如 AndroMDA,都利用EMF在架極模型的基 礎(chǔ)上生成代碼。說(shuō)到在領(lǐng)域?qū)泳帉?xiě)委托類(lèi),我看到開(kāi)發(fā)人員手動(dòng)編寫(xiě)這些類(lèi)(大多是從無(wú)到有地寫(xiě) 完第一個(gè),接著用發(fā)制并粘貼”的模式來(lái)為其它的領(lǐng)域?qū)ο髣?chuàng)建所需的委托 類(lèi))。由驅(qū)些類(lèi)大部分都是領(lǐng)域類(lèi)的外觀(guān),日門(mén)很適合代碼生成。代碼生成是長(zhǎng) 遠(yuǎn)的解決辦法,盡管建立并測(cè)試代碼生成器(引擎)增加了初期的投入(伽量和 時(shí)間)。對(duì)生成的測(cè)試類(lèi)來(lái)說(shuō),一個(gè)好的選擇就是在需要進(jìn)行單元測(cè)試的主類(lèi)中,為帶有復(fù) 雜業(yè)務(wù)邏輯的方法創(chuàng)建抽象方法。這樣,開(kāi)發(fā)人員能繼承生成的測(cè)試基類(lèi),然后實(shí) 現(xiàn)不能自動(dòng)生成的自定義業(yè)務(wù)邏輯。RW,這個(gè)方法也適用丁佚何有不能自 動(dòng)創(chuàng)建 測(cè)試邏輯

46、的測(cè)試方法。對(duì)編寫(xiě)代碼生成器來(lái)說(shuō),腳列言是一個(gè)更好的選擇,因?yàn)樗鼈冮_(kāi)銷(xiāo)少,還支持模 板創(chuàng)建和自定義選項(xiàng)。如果我們?cè)贒DD項(xiàng)目中充分利用代 碼生成,我們只需要從 無(wú)到有地編寫(xiě)少量的代碼。必須從無(wú)到有進(jìn)行創(chuàng)建的工件有: XSD領(lǐng)域?qū)ο蠓?wù)一旦我們定義了 XSD和Java類(lèi),我門(mén)可以生成下列全部或大部分的 類(lèi)和配置文 件: DAO接口和實(shí)現(xiàn)類(lèi) 工廠(chǎng)資源庫(kù) 領(lǐng)域代理(如果有必要) 外觀(guān)(包括 EJB和 WebService 類(lèi)) DTO上述類(lèi)的單元測(cè)試(包括測(cè)試類(lèi)和測(cè)試數(shù)據(jù)) Spring配置文件表4列出了 Web應(yīng)用架極中不同的層,以及那些層中能生成什么工件Java類(lèi)或 XML文件)。表4. DDD

47、實(shí)現(xiàn)項(xiàng)目中的代碼生成層/功能模式你寫(xiě)的代碼生成的代碼框架數(shù)據(jù)訪(fǎng)問(wèn)DAO/資源庫(kù)DAO 接口,DAO實(shí)現(xiàn)類(lèi),DAOTest ,測(cè)試種子數(shù)據(jù)Unitils, DBUnit領(lǐng)域DO領(lǐng)域類(lèi)DomainTest持久化ORM領(lǐng)域類(lèi)ORM映射,ORM映射測(cè)試Hibernate, ORMUnit數(shù)據(jù)傳輸DTOXSDDTOJAXBDTO組裝組裝映射DO-DTO 映射文件Dozer委托業(yè)務(wù)委托DO到DTO的轉(zhuǎn)換代碼外觀(guān)外觀(guān)遠(yuǎn)程服務(wù),EJB,Web Service控制器MVC控制器映射文件Struts/Spring MVC表示層MVC視圖配置文件Spring MVC委托層是唯一同時(shí)理解領(lǐng)域?qū)ο蠛虳TO的層。其它層,

48、例如持久層,不應(yīng)該察覺(jué)到 DTO。重構(gòu)重極就是改變或調(diào)整應(yīng)用代碼,但不修改應(yīng)用的功能或行為。重極可以是設(shè)計(jì)相關(guān) 的,也可以是代碼相關(guān)的。設(shè)計(jì)重極是為了不斷完善模型、重極代碼來(lái)提升領(lǐng)域模 型。由丁重極的迭代性和 領(lǐng)域建模不斷演進(jìn)的性質(zhì),重極在DDD項(xiàng)目中發(fā)揮著重要作 用。將重極佚務(wù)集成到項(xiàng)目中的方法之一是在 項(xiàng)目的每次迭代中添加重極 環(huán)節(jié),重 極結(jié)束之后才算完成迭代。理想情況下,每項(xiàng)開(kāi)發(fā)佚務(wù)之前和之后都應(yīng)該進(jìn)行重 極。進(jìn)行重極應(yīng)該有嚴(yán)格的規(guī)定。結(jié)合使用重極、CI和單元測(cè)試,以確保代碼變化不會(huì) 破壞佚何功能,同時(shí),徹的變化要有助丁以后的代 碼和性能改進(jìn)。自動(dòng)化測(cè)試在重極應(yīng)用代碼中發(fā)揮著至關(guān)重要的作

49、用。沒(méi)有良好的自動(dòng)化測(cè)試和 測(cè)試驅(qū)動(dòng)開(kāi)發(fā)TDD)實(shí)踐,重極可能會(huì)產(chǎn)生反面的效果,因?yàn)闆](méi)有自動(dòng)化的方式去 驗(yàn)證作為重極一部分的設(shè)計(jì)和代碼并變化沒(méi)有改變行為、或破壞功能。像Eclipse這 樣的工具有助丁用迭代的方式和作 為開(kāi)發(fā)一部分的重極來(lái)實(shí)現(xiàn)領(lǐng)域 模型。Eclipse有一些功能,比如把一個(gè)方法提取或移 動(dòng)到不同的類(lèi)中,或?qū)⒁粋€(gè)方 法下推 到子類(lèi)中。也有幾個(gè)Eclipse代碼分析插件有助丁處理代碼依賴(lài)關(guān)系、識(shí)別 DDD反模式。我做項(xiàng)目的設(shè)計(jì)和代碼審查時(shí)、都是依靠插件JDepend、Classycle和 Metrics來(lái)評(píng)估應(yīng)用中領(lǐng)域和其它模 塊的質(zhì)量。Chris Richardson談到運(yùn)用代碼

50、重極、以使用Eclipse提供的重極功能將 過(guò)程設(shè)計(jì)轉(zhuǎn) 變?yōu)橐粋€(gè)OO設(shè)計(jì)。單元測(cè)試/持續(xù)集成我們剛才談到的目標(biāo)之一是領(lǐng)域類(lèi)應(yīng)該(在最初的開(kāi)發(fā)階段,以及隨后重極已有代 碼時(shí))單元可測(cè),而不過(guò)多依賴(lài)丁容器或其它基 礎(chǔ)設(shè)施代碼。TDD方法有 助丁團(tuán) 隊(duì)盡早地找出佚何 設(shè)計(jì)問(wèn)題,并有助丁驗(yàn)證代碼與領(lǐng)域模型在保持一致。DDD對(duì)測(cè) 試先行開(kāi)發(fā)來(lái)說(shuō)是很理想的,因?yàn)闋顟B(tài)和行為都包含在領(lǐng)域類(lèi)中,而且單獨(dú)測(cè)試 它們應(yīng)該是容易的。測(cè)試領(lǐng)域模型的狀態(tài)和行為,乂不太過(guò)關(guān)注丁數(shù)據(jù)訪(fǎng)問(wèn)或持久 化的實(shí)現(xiàn)細(xì)節(jié)是很重要的。單元測(cè)試框架,比如JUnit或TestNG,都是實(shí)現(xiàn)和處理領(lǐng)域模型很棒的工具。其它 測(cè)試框架,像DBUnit

51、和Unitils,也可用來(lái)測(cè)試領(lǐng)域?qū)?,尤其是把測(cè)試數(shù)據(jù)注入到 DAO類(lèi)中。對(duì)在單元測(cè)試類(lèi)中增加測(cè)試數(shù)據(jù)來(lái)說(shuō),這將大大減少編寫(xiě)額外的代碼。 模擬對(duì)象Mock objects)向羊有利丁單獨(dú)測(cè)試領(lǐng)域?qū)ο?。但是在領(lǐng)域?qū)硬灰獮E用模 擬對(duì)象是很重要的。如果有其他 測(cè)試領(lǐng)域類(lèi)的簡(jiǎn)單方法,你應(yīng)該使用這些方法來(lái)代 替使用 模擬對(duì)象。比如說(shuō),如果你能使用真實(shí)的后端DAO類(lèi)(而不是豐堀的DAO 實(shí)現(xiàn))和內(nèi)存HSQL數(shù)據(jù)庫(kù)(而不是真實(shí)的數(shù)據(jù)庫(kù))測(cè)試一個(gè)實(shí)體類(lèi),能使領(lǐng)域?qū)訂?元測(cè)試運(yùn)行得更快,而運(yùn)行得更快正好是使用模 擬對(duì)象潛在的主要想法。這樣,你 將能測(cè)試領(lǐng)域?qū)ο笾g的協(xié)作(交互),以及窗之間交換的狀態(tài)(數(shù)據(jù))。使

52、用莫 擬對(duì)象,我們則只能測(cè)試領(lǐng)域?qū)ο笾g的交互。一旦開(kāi)發(fā)佚務(wù)完成,所有在開(kāi)發(fā)階段創(chuàng)建的單元測(cè)試和集成測(cè)試(不管有沒(méi)有使用 TDD做法)都將以 自動(dòng)化測(cè)試套件的一部分。這些測(cè)試用應(yīng)該經(jīng)常進(jìn)行維護(hù),并 經(jīng)常在本地或更高一級(jí)的開(kāi)發(fā)環(huán)境中執(zhí)行,以便找出新的代碼變化是否在領(lǐng)域類(lèi) 中引入了 BugEric Evans在他的坐中提到了 CI,CI應(yīng)該始終運(yùn)用在界定的上下文中,應(yīng)該包括人和代碼的同步。像CruiseControl和Hudson這些CI工具可用來(lái)建立一個(gè)自 動(dòng) 化極建和測(cè)試的環(huán)境,來(lái)運(yùn)行應(yīng)用極建腳本(使用Ant或Maven這些極建工具創(chuàng)建) 從SCM倉(cāng)庫(kù)中(像CVS、Subversion等)檢出

53、代碼,編譯領(lǐng)域類(lèi)(以加用中的其它 類(lèi)),并在沒(méi)有極建昔誤的情況下自動(dòng)運(yùn)行所有的測(cè)試 單元測(cè)試和集成測(cè)試)CI 工具還可以設(shè)置在有佚何極建或 測(cè)試錯(cuò)誤時(shí)(迎E-mail或RSS Feeds)通矢咬目 團(tuán)隊(duì)。部署領(lǐng)域模型絕對(duì)不會(huì)是靜態(tài)的;傾目生命周期中,它們會(huì)隨著業(yè)務(wù)需求的演變、新 項(xiàng)目中新需求的提出而 發(fā)生變化。此外,隨著你開(kāi)發(fā)和實(shí)現(xiàn)領(lǐng)域模型,你能不斷學(xué) 習(xí)和提高,而且你也想在已有的模型中運(yùn)用新的知 識(shí)。打包、部署領(lǐng)域類(lèi)的時(shí)候,隔離很關(guān)鍵。電領(lǐng)域?qū)右蕾?lài)丁 DAO層的一面,而服務(wù) 外觀(guān)層乂依賴(lài)丁 DAO層的另一面(參見(jiàn)圖2-應(yīng)用架極圖),所旌些領(lǐng)域類(lèi)打包、 部署為一或多個(gè)模塊來(lái)處理依賴(lài)關(guān)系很有意義

54、。DI、AOP和工廠(chǎng)這些設(shè)計(jì)模式在設(shè)計(jì)階段減少了對(duì)象之間的耦合,并使應(yīng)用模塊 化;OSGi (以前被稱(chēng)為開(kāi)放服務(wù)網(wǎng)關(guān)規(guī)范)則在運(yùn)行時(shí)處理模塊化。OSG i正在成為 打包、發(fā)布企業(yè)應(yīng)用的標(biāo)準(zhǔn)機(jī)制。它能很好地處理模塊之間的依賴(lài)關(guān)系。我們還能 用OSGi來(lái)進(jìn)行領(lǐng)域模型的版本處理。我們可以把DAO類(lèi)打包到一個(gè)OSGi的Bundle DAO Bundle)中,把月爵外觀(guān)類(lèi)打 包到另一個(gè)Bundle (月盼Bundle)中,所KDAO或服務(wù)實(shí)現(xiàn)進(jìn)行了修改,或是部署 了應(yīng)用的不同版本,由丁 OSGi,應(yīng)用都不需要重啟。如果我們?yōu)榱讼蚝笃澣荩仨?支持某些領(lǐng)域?qū)ο笠延械陌姹竞托碌陌姹?,那?們也可以部署相同領(lǐng)

55、域類(lèi)的兩個(gè) 不同版本。為了利用OSGi的能力,應(yīng)用對(duì)象在消費(fèi)之前(即在客戶(hù)端能查找到它們之前),應(yīng) 該在OSGi平臺(tái)中進(jìn)行注冊(cè)。這意味著我們必須使用OSGi的API進(jìn)行注冊(cè),我們還 必須處理使用OSGi容器啟動(dòng)和通知服務(wù)時(shí)的失敗場(chǎng)景。Spring Dynamic Modules 框架對(duì)該領(lǐng)域很有利,它允許在應(yīng)用中導(dǎo)出或?qū)胴螌?duì)象類(lèi)型,而不改變佚何代 碼OSpring DM還提供測(cè)試類(lèi),以在容器外運(yùn)行OSGi集成測(cè)試。比如說(shuō),能從IDE中直 接用AbstractOsgiTests運(yùn)行集成測(cè)試。設(shè)置由測(cè)試基礎(chǔ)設(shè)施來(lái)處理,所以我們不需 要為測(cè)試編寫(xiě)MANIFEST.MF文件,或者進(jìn)行佚何的打包或部署。該框架支持大部 分目前可用的 OSGi 實(shí)現(xiàn) Equinox、Knopflerfish 和 Apache Felix)。貸款處理應(yīng)用使用OSGh Spring DM、Equinox容器來(lái)處理模塊級(jí)別的依賴(lài)關(guān)系,以 及領(lǐng)域和其它模 塊的部署。LoanAppDeploymen

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶(hù)所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶(hù)上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶(hù)上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶(hù)因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論