小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

貧血模式or領(lǐng)域模式(轉(zhuǎn)載)

 richsky 2012-04-23

轉(zhuǎn)自:http://mabusyao./blog/467704

最近taowen同學(xué)連續(xù)發(fā)起了兩起關(guān)于貧血模型和領(lǐng)域模型的討論,引起了大家的廣泛熱烈的討論,但是討論(或者說是爭論)的結(jié)果到底怎樣,我想值 得商榷。問題是大家對貧血模型和領(lǐng)域模型都有自己的看法,如果沒有對此達到概念上的共識,那么討論的結(jié)果應(yīng)該可想而知,討論的收獲也是有的,至少知道了分 歧的存在。為了使問題具有確定性,我想從一個簡單例子著手,用我對貧血模型和領(lǐng)域模型的概念來分別實現(xiàn)例子。至于我的理解對與否,大家可以做評判,至少有 個可以評判的標準在這。

一個例子


我要舉的是一個銀行轉(zhuǎn)帳的例子,又是一個被用濫了的例子。但即使這個例子也不是自己想出來的,而是剽竊的<<POJOs in Action>>中的例子,原諒我可憐的想像力 。當錢從一個帳戶轉(zhuǎn)到另一個帳戶時,轉(zhuǎn)帳的金額不能超過第一個帳戶的存款余額,余額總數(shù)不能變,錢只是從一個賬戶流向另一個帳戶,因此它們必須在一個事務(wù)內(nèi)完成,每次事務(wù)成功完成都要記錄此次轉(zhuǎn)帳事務(wù),這是所有的規(guī)則。

 

 

貧血模型


我們首先用貧血模型來實現(xiàn)。所謂貧血模型就是模型對象之間存在完整的關(guān)聯(lián)(可能存在多余的關(guān)聯(lián)),但是對象除了get和set方外外幾乎就沒有其它的方 法,整個對象充當?shù)木褪且粋€數(shù)據(jù)容器,用C語言的話來說就是一個結(jié)構(gòu)體,所有的業(yè)務(wù)方法都在一個無狀態(tài)的Service類中實現(xiàn),Service類僅僅包 含一些行為。這是Java Web程序采用的最常用開發(fā)模型,你可能采用的就是這種方法,雖然可能不知道它有個&ldquo;貧血模型&rdquo;的稱號,這要多 虧Martin Flower(這個家伙慣會發(fā)明術(shù)語!)。

 

包結(jié)構(gòu)


在討論具體的實現(xiàn)之前,我們先來看來貧血模型的包結(jié)構(gòu),以便對此有個大概的了解。
   

貧血模型的實現(xiàn)一般包括如下包:

  • dao:負責(zé)持久化邏輯
  • model:包含數(shù)據(jù)對象,是service操縱的對象
  • service:放置所有的服務(wù)類,其中包含了所有的業(yè)務(wù)邏輯
  • facade:提供對UI層訪問的入口

代碼實現(xiàn)


先看model包的兩個類,Account和TransferTransaction對象,分別代表帳戶和一次轉(zhuǎn)賬事務(wù)。由于它們不包含業(yè)務(wù)邏輯,就是一個普通的Java Bean,下面的代碼省略了get和set方法。

Java代碼
  1. public   class  Account {  
  2.     private  String accountId;  
  3.     private  BigDecimal balance;  
  4.   
  5.     public  Account() {}  
  6.     public  Account(String accountId, BigDecimal balance) {  
  7.         this .accountId = accountId;  
  8.         this .balance = balance;  
  9.     }  
  10.     // getter and setter ....   
  11.   
  12. }   
Java代碼
  1. public   class  TransferTransaction {  
  2.     private  Date timestamp;  
  3.     private  String fromAccountId;  
  4.     private  String toAccountId;  
  5.     private  BigDecimal amount;    
  6.   
  7.     public  TransferTransaction() {}  
  8.   
  9.     public  TransferTransaction(String fromAccountId, String toAccountId, BigDecimal amount, Date timestamp) {  
  10.         this .fromAccountId = fromAccountId;  
  11.         this .toAccountId = toAccountId;  
  12.         this .amount = amount;  
  13.         this .timestamp = timestamp;  
  14.     }  
  15.   
  16.     // getter and setter ....   
  17. }  
這兩個類沒什么可說的,它們就是一些數(shù)據(jù)容器。接下來看service包中TransferService接口和它的實現(xiàn) TransferServiceImpl。TransferService定義了轉(zhuǎn)賬服務(wù)的接口,TransferServiceImpl則提供了轉(zhuǎn)賬服 務(wù)的實現(xiàn)。
Java代碼
  1. public   interface  TransferService {  
  2.     TransferTransaction transfer(String fromAccountId, String toAccountId, BigDecimal amount)   
  3.             throws  AccountNotExistedException, AccountUnderflowException;  
  4. }   
Java代碼
  1. public   class  TransferServiceImpl  implements  TransferService {  
  2.     private  AccountDAO accountDAO;  
  3.     private  TransferTransactionDAO transferTransactionDAO;  
  4.   
  5.     public  TransferServiceImpl(AccountDAO accountDAO,   
  6.             TransferTransactionDAO transferTransactionDAO) {  
  7.         this .accountDAO = accountDAO;  
  8.         this .transferTransactionDAO = transferTransactionDAO;  
  9.   
  10.     }  
  11.   
  12.     public  TransferTransaction transfer(String fromAccountId, String toAccountId,  
  13.             BigDecimal amount) throws  AccountNotExistedException, AccountUnderflowException {     
  14.     Validate.isTrue(amount.compareTo(BigDecimal.ZERO) > 0 );        
  15.   
  16.         Account fromAccount = accountDAO.findAccount(fromAccountId);  
  17.         if  (fromAccount ==  null  throw   new  AccountNotExistedException(fromAccountId);  
  18.         if  (fromAccount.getBalance().compareTo(amount) <  0 ) {  
  19.             throw   new  AccountUnderflowException(fromAccount, amount);  
  20.         }         
  21.   
  22.         Account toAccount = accountDAO.findAccount(toAccountId);  
  23.         if  (toAccount ==  null  throw   new  AccountNotExistedException(toAccountId);  
  24.         fromAccount.setBalance(fromAccount.getBalance().subtract(amount));  
  25.         toAccount.setBalance(toAccount.getBalance().add(amount));                 
  26.   
  27.         accountDAO.updateAccount(fromAccount);      // 對Hibernate來說這不是必須的   
  28.         accountDAO.updateAccount(toAccount);        // 對Hibernate來說這不是必須的   
  29.         return  transferTransactionDAO.create(fromAccountId, toAccountId, amount);  
  30.     }  
  31. }  
TransferServiceImpl類使用了AccountDAO和TranferTransactionDAO,它的transfer方法負責(zé)整個 轉(zhuǎn)帳操作,它首先判斷轉(zhuǎn)帳的金額必須大于0,然后判斷fromAccountId和toAccountId是一個存在的Account的 accountId,如果不存在拋AccountNotExsitedException。接著判斷轉(zhuǎn)帳的金額是否大于fromAccount的余額,如 果是則拋AccountUnderflowException。接著分別調(diào)用fromAccount和toAccount的setBalance來更新它 們的余額。最后保存到數(shù)據(jù)庫并記錄交易。TransferServiceImpl負責(zé)所有的業(yè)務(wù)邏輯,驗證是否超額提取并更新帳戶余額。一切并不復(fù)雜,對 于這個例子來說,貧血模型工作得非常好!這是因為這個例子相當簡單,業(yè)務(wù)邏輯也不復(fù)雜,一旦業(yè)務(wù)邏輯變得復(fù)雜,TransferServiceImpl就 會膨脹。

 

優(yōu)缺點

 

貧血模型的優(yōu)點是很明顯的:

  1. 被許多程序員所掌握,許多教材采用的是這種模型,對于初學(xué)者,這種模型很自然,甚至被很多人認為是java中最正統(tǒng)的模型。
  2. 它非常簡單,對于并不復(fù)雜的業(yè)務(wù)(轉(zhuǎn)帳業(yè)務(wù)),它工作得很好,開發(fā)起來非常迅速。它似乎也不需要對領(lǐng)域的充分了解,只要給出要實現(xiàn)功能的每一個步驟,就能實現(xiàn)它。
  3. 事務(wù)邊界相當清楚,一般來說service的每個方法都可以看成一個事務(wù),因為通常Service的每個方法對應(yīng)著一個用例。(在這個例子中我使用了facade作為事務(wù)邊界,后面我要講這個是多余的)


其缺點為也是很明顯的:

  1. 所有的業(yè)務(wù)都在service中處理,當業(yè)越來越復(fù)雜時,service會變得越來越龐大,最終難以理解和維護。
  2. 將所有的業(yè)務(wù)放在無狀態(tài)的service中實際上是一個過程化的設(shè)計,它在組織復(fù)雜的業(yè)務(wù)存在天然的劣勢,隨著業(yè)務(wù)的復(fù)雜,業(yè)務(wù)會在service中多個方法間重復(fù)。
  3. 當添加一個新的UI時,很多業(yè)務(wù)邏輯得重新寫。例如,當要提供Web Service的接口時,原先為Web界面提供的service就很難重用,導(dǎo)致重復(fù)的業(yè)務(wù)邏輯(在貧血模型的分層圖中可以看得更清楚),如何保持業(yè)務(wù)邏輯一致是很大的挑戰(zhàn)。

 


領(lǐng)域模型

 


接下來看看領(lǐng)域驅(qū)動模型,與貧血模型相反,領(lǐng)域模型要承擔關(guān)鍵業(yè)務(wù)邏輯,業(yè)務(wù)邏輯在多個領(lǐng)域?qū)ο笾g分配,而Service只是完成一些不適合放在模型中的業(yè)務(wù)邏輯,它是非常薄的一層,它指揮多個模型對象來完成業(yè)務(wù)功能。

 

包結(jié)構(gòu)

 

領(lǐng)域模型的實現(xiàn)一般包含如下包:

  • infrastructure: 代表基礎(chǔ)設(shè)施層,一般負責(zé)對象的持久化。
  • domain:代表領(lǐng)域?qū)?。domain包中包括兩個子包,分別是model和service。model中包含模型對 象,Repository(DAO)接口。它負責(zé)關(guān)鍵業(yè)務(wù)邏輯。service包為一系列的領(lǐng)域服務(wù),之所以需要service,按照DDD的觀點,是因為領(lǐng)域中的某些概念本質(zhì)是一些行為,并且不便放入某個模型對象中。比如轉(zhuǎn)帳操作,它是一個行為,并且它涉及三個對 象,fromAccount,toAccount和TransferTransaction,將它放入任一個對象中都不好。
  • application: 代表應(yīng)用層,它的主要提供對UI層的統(tǒng)一訪問接口,并作為事務(wù)界限。


 

代碼實現(xiàn)

 

現(xiàn)在來看實現(xiàn),照例先看model中的對象:

Java代碼
  1. public   class  Account {  
  2.     private  String accountId;  
  3.     private  BigDecimal balance;  
  4.       
  5.     private  OverdraftPolicy overdraftPolicy = NoOverdraftPolicy.INSTANCE;  
  6.       
  7.     public  Account() {}  
  8.       
  9.     public  Account(String accountId, BigDecimal balance) {  
  10.         Validate.notEmpty(accountId);  
  11.         Validate.isTrue(balance == null  || balance.compareTo(BigDecimal.ZERO) >=  0 );  
  12.           
  13.         this .accountId = accountId;  
  14.         this .balance = balance ==  null  ? BigDecimal.ZERO : balance;  
  15.     }  
  16.       
  17.     public  String getAccountId() {  
  18.         return  accountId;  
  19.     }  
  20.   
  21.     public  BigDecimal getBalance() {  
  22.         return  balance;  
  23.     }  
  24.       
  25.     public   void  debit(BigDecimal amount)  throws  AccountUnderflowException {  
  26.         Validate.isTrue(amount.compareTo(BigDecimal.ZERO) > 0 );  
  27.           
  28.         if  (!overdraftPolicy.isAllowed( this , amount)) {  
  29.             throw   new  AccountUnderflowException( this , amount);  
  30.         }  
  31.         balance = balance.subtract(amount);  
  32.     }  
  33.       
  34.     public   void  credit(BigDecimal amount) {  
  35.         Validate.isTrue(amount.compareTo(BigDecimal.ZERO) > 0 );  
  36.           
  37.         balance = balance.add(amount);  
  38.     }  
  39.       
  40. }  
與貧血模型的區(qū)別在于Account類中包含業(yè)務(wù)方法(credit,debit),注意沒有set方法,對Account的更新是通過業(yè)務(wù)方法來更新 的。由于“不允許從帳戶取出大于存款余額的資金”是一條重要規(guī)則,將它放在一個單獨的接口OverdraftPolicy中,也提供了靈活性,當業(yè)務(wù)規(guī)則 變化時,只需要改變這個實現(xiàn)就可以了。

TransferServiceImpl類:

Java代碼
  1. public   class  TransferServiceImpl  implements  TransferService {  
  2.     private  AccountRepository accountRepository;  
  3.     private  TransferTransactionRepository transferTransactionRepository;  
  4.       
  5.     public  TransferServiceImpl(AccountRepository accountRepository,   
  6.             TransferTransactionRepository transferTransactionRepository) {  
  7.         this .accountRepository = accountRepository;  
  8.         this .transferTransactionRepository = transferTransactionRepository;  
  9.     }  
  10.       
  11.     public  TransferTransaction transfer(String fromAccountId, String toAccountId,  
  12.             BigDecimal amount) throws  AccountNotExistedException, AccountUnderflowException {  
  13.         Account fromAccount = accountRepository.findAccount(fromAccountId);  
  14.         if  (fromAccount ==  null  throw   new  AccountNotExistedException(fromAccountId);  
  15.         Account toAccount = accountRepository.findAccount(toAccountId);  
  16.         if  (toAccount ==  null  throw   new  AccountNotExistedException(toAccountId);  
  17.   
  18.         fromAccount.debit(amount);  
  19.         toAccount.credit(amount);  
  20.           
  21.         accountRepository.updateAccount(fromAccount);   // 對Hibernate來說這不是必須的   
  22.         accountRepository.updateAccount(toAccount);     // 對Hibernate來說這不是必須的   
  23.         return  transferTransactionRepository.create(fromAccountId, toAccountId, amount);  
  24.     }  
  25.       
  26. }  
與貧血模型中的TransferServiceImpl相比,最主要的改變在于業(yè)務(wù)邏輯被移走了,由Account類來實現(xiàn)。對于這樣一個簡單的例子,領(lǐng)域模型沒有太多優(yōu)勢,但是仍然可以看到代碼的實現(xiàn)要簡單一些。當業(yè)務(wù)變得復(fù)雜之后,領(lǐng)域模型的優(yōu)勢就體現(xiàn)出來了。

 

優(yōu)缺點

 

其優(yōu)點是:

  1. 領(lǐng)域模型采用OO設(shè)計,通過將職責(zé)分配到相應(yīng)的模型對象或Service,可以很好的組織業(yè)務(wù)邏輯,當業(yè)務(wù)變得復(fù)雜時,領(lǐng)域模型顯出巨大的優(yōu)勢。
  2. 當需要多個UI接口時,領(lǐng)域模型可以重用,并且業(yè)務(wù)邏輯只在領(lǐng)域?qū)又谐霈F(xiàn),這使得很容易對多個UI接口保持業(yè)務(wù)邏輯的一致(從領(lǐng)域模型的分層圖可以看得更清楚)。

其缺點是:

  1. 對程序員的要求較高,初學(xué)者對這種將職責(zé)分配到多個協(xié)作對象中的方式感到極不適應(yīng)。
  2. 領(lǐng)域驅(qū)動建模要求對領(lǐng)域模型完整而透徹的了解,只給出一個用例的實現(xiàn)步驟是無法得到領(lǐng)域模型的,這需要和領(lǐng)域?qū)<业某浞钟懻?。錯誤的領(lǐng)域模型對項目的危害非常之大,而實現(xiàn)一個好的領(lǐng)域模型非常困難。
  3. 對于簡單的軟件,使用領(lǐng)域模型,顯得有些殺雞用牛刀了。

 

我的看法

 

 

這部分我將提出一些可能存在爭議的問題并提出自己的看法。

 

軟件分層


理解軟件分層、明晰每層的職責(zé)對于理解領(lǐng)域模型以及代碼實現(xiàn)是有好處的。軟件一般分為四層,分別為表示層,應(yīng)用層,領(lǐng)域?qū)雍突A(chǔ)設(shè)施層。軟件領(lǐng)域中另外一個著名的分層是TCP/IP分層,分為應(yīng)用層,運輸層,網(wǎng)際層和網(wǎng)絡(luò)接口層。我發(fā)現(xiàn)它們之間存在對應(yīng)關(guān)系,見下表:

 

TCP/IP分層 軟件分層
    表示層 負責(zé)向用戶顯示信息。
應(yīng)用層 負責(zé)處理特定的應(yīng)用程序細節(jié)。如FTP,SMTP等協(xié)議。 應(yīng)用層 定義軟件可以完成的工作,指揮領(lǐng)域?qū)拥膶ο髞斫鉀Q問題。它不負責(zé)業(yè)務(wù)邏輯,是很薄的一層。
運輸層 兩臺主機上的應(yīng)用程序提供端到端的通信。主要包括TCP,UDP協(xié)議。 領(lǐng)域?qū)?/strong> 負責(zé)業(yè)務(wù)邏輯,是業(yè)務(wù)軟件的核心。
網(wǎng)際層 處理分組在網(wǎng)絡(luò)中的活動,例如分組的選路。主要包括IP協(xié)議。
網(wǎng)絡(luò)接口層 操作系統(tǒng)中的設(shè)備驅(qū)動程序和計算機中對應(yīng)的網(wǎng)絡(luò)接口卡。它們一起處理與電纜(或其他任何傳輸媒介)的物理接口細節(jié)。 基礎(chǔ)設(shè)施層 為上層提供通用技術(shù)能力,如消息發(fā)送,數(shù)據(jù)持久化等。

 

對于TCP/IP來說,運輸層和網(wǎng)際層是最核心的,這也是TCP/IP名字的由來,就像領(lǐng)域?qū)右彩擒浖詈诵牡囊粚印?梢钥闯鲱I(lǐng)域模型的包結(jié)構(gòu)與軟 件分層是一致的。在軟件分層中,表示層、領(lǐng)域?qū)雍突A(chǔ)設(shè)施層都容易理解,難理解的是應(yīng)用層,很容易和領(lǐng)域?qū)又蠸ervice混淆。領(lǐng)域Service屬于 領(lǐng)域?qū)?,它需要承擔部分業(yè)務(wù)概念,并且這個業(yè)務(wù)概念不易放入模型對象中。應(yīng)用層服務(wù)不承擔任何業(yè)務(wù)邏輯和業(yè)務(wù)概念,它只是調(diào)用領(lǐng)域?qū)又械膶ο螅ǚ?wù)和模 型)來完成自己的功能。應(yīng)用層為表示層提供接口,當UI接口改變一般也會導(dǎo)致應(yīng)用層接口改變,也可能當UI接口很相似時應(yīng)用層接口不用改變,但是領(lǐng)域?qū)?(包括領(lǐng)域服務(wù))不能變動。例如一個應(yīng)用同時提供Web接口和Web Service接口時,兩者的應(yīng)用層接口一般不同,這是因為Web Service的接口一般要粗一些。可以和TCP/IP的層模型進行類比,開發(fā)一個FTP程序和MSN聊天程序,它們的應(yīng)用層不同,但是可以同樣利用 TCP/IP協(xié)議,TCP/IP協(xié)議不用變。與軟件分層不同的是,當同樣開發(fā)一個FTP程序時,如果只是UI接口不同,一個是命令行程序,一個是圖形界 面,應(yīng)用層不用變(利用的都是FTP服務(wù))。下圖給出領(lǐng)域模型中的分層:

 

 

 

Repository接口屬于領(lǐng)域?qū)?/strong>

 

可能有人會將Repository接口,相當于貧血模型中的DAO接口,歸于基礎(chǔ)設(shè)施層,畢竟在貧血模型中DAO是和它的實現(xiàn)放在一起。這就涉及 Repository 接口到底和誰比較密切?應(yīng)該和domain層比較密切,因為Repository接口是由domain層來定義的。用TCP/IP來類比,網(wǎng)際層支持標準 以太網(wǎng)、令牌環(huán)等網(wǎng)絡(luò)接口,支持接口是在網(wǎng)際層中定義的,沒有在網(wǎng)際層定義的網(wǎng)絡(luò)接口是不能被網(wǎng)際層訪問的。那么為什么在貧血模型中DAO的接口沒有放在 model包中,這是因為貧血模型中DAO的接口是由service來定義的,但是為什么DAO接口也沒有放在service包中,我無法解釋,按照我的 觀點DAO接口放在service包中要更好一些,將DAO接口放在dao包或許有名稱上對應(yīng)的考慮。對于領(lǐng)域模型,將Repository接口放入 infrastructure包中會引入包的循環(huán)依賴,Repository依賴Domain,Domain依賴Repository。然而對于貧血模 型,將DAO接口放入dao包中則不會引入包循環(huán)依賴,只有service對DAO和model的依賴,而沒有反方向的依賴,這也導(dǎo)致service包很 不穩(wěn)定,service又正是放置業(yè)務(wù)邏輯的地方。JDepend這個工具可以檢測包的依賴關(guān)系。

 

貧血模型中Facade有何用?

 

我以前的做一個項目使用的就是貧血模型,使用了service和facade,當我們討論service和facade有什么區(qū)別時,很少有人清 楚,最終結(jié)果facade就是一個空殼,它除了將方法實現(xiàn)委托給相應(yīng)的service方法,不做任何事,它們的接口中的方法都一樣。Facade應(yīng)該是主 要充當遠程訪問的門面,這在EJB時代相當普遍,自從Rod Johson叫嚷without EJB之后,大家對EJB的熱情降了很多,對許多使用貧血模型的應(yīng)用程序來說,facade是沒有必要的。貧血模型中的service在本質(zhì)上屬于應(yīng)用層 的東西。當然如果確實需要提供遠程訪問,那么遠程Facade(或許叫做Remote Service更好)也是很有用的,但是它仍然屬于應(yīng)用層,只不過在技術(shù)層面上將它的實現(xiàn)委托給對應(yīng)的Service。下圖是貧血模型的分層:

 

貧血模型分層

 

從上面的分層可以看出貧血模型實際上相當于取消掉了領(lǐng)域?qū)?,因為領(lǐng)域?qū)硬]有包含業(yè)務(wù)邏輯。

 

 

DAO到底有沒有必要?

 

貧血模型中的DAO或領(lǐng)域模型中的Repository到底有沒有必要?有人認為DAO或者說Repository是充血模型的大敵,對此我無論如 何也不贊同。DAO或Repository是負責(zé)持久化邏輯的,如果取消掉DAO或Repository,將持久化邏輯直接寫入到model對象中,勢必 造成model對象承擔不必要的職責(zé)。雖然現(xiàn)在的ORM框架已經(jīng)做得很好了,持久化邏輯還是需要大量的代碼,持久化邏輯的摻入會使model中的業(yè)務(wù)邏輯 變得模糊。允許去掉DAO的一個必要條件就是Java的的持久化框架必須足夠先進,持久化邏輯的引入不會干擾業(yè)務(wù)邏輯,我認為這在很長一段時間內(nèi)將無法做 到。在rails中能夠?qū)AO去掉的原因就是rail中實現(xiàn)持久化邏輯的代碼很簡潔直觀,這也與ruby的表達能力強有關(guān)系。DAO的另外一個好處隔離 數(shù)據(jù)庫,這可以支持多個數(shù)據(jù)庫,甚至可以支持文件存儲?;贒AO的這些優(yōu)點,我認為,即使將來Java的持久化框架做得足夠優(yōu)秀,使用DAO將持久化邏 輯從業(yè)務(wù)邏輯中分離開來還是十分必要的,況且它們本身就應(yīng)該分離。

 

 

 

結(jié)束語

 

在這篇文章里,我使用了一個轉(zhuǎn)帳例子來描述領(lǐng)域模型和貧血模型的不同,實現(xiàn)代碼可以從附件中下載,我推薦你看下附件代碼,這會對領(lǐng)域模型和貧血模型 有個更清楚的認識。我談到了軟件的分層,以及貧血模型和領(lǐng)域模型的實現(xiàn)又是怎樣對應(yīng)到這些層上去的,最后是對DAO(或Repository)的討論。以 上只是我個人觀點,如有不同意見歡迎指出。

 

 

 

 

 

爭論的焦點到了DAO上啊,呵呵。確實,實踐中很多項目最終實施DDD的結(jié)果就是把所有的DAO重命名為Repository。但是我認為DAO和Repository很像,但是不是一個東西,因為它們出發(fā)點不同。 

為什么要有DAO? 
因為之前,很早之前,我們對于框架中立性還很受用。DAO給了我們可以隨時把Hibernate換成ibatis的幻覺,所以我們要有一個地方隔離了框架。 
而且DAO集中了所有的查詢,方便了性能調(diào)優(yōu)人員。同時也鼓勵了查詢的重用,同樣方便了調(diào)優(yōu)。 

為什么要有Repository? 
在我看來,與其說PublicationRepository,不如說Publications。Repository是一個集合對象,它封裝了集合的邏輯。因為它具有封裝性,所以它應(yīng)該負責(zé)保持這個集合的狀態(tài),比如拒絕一些非法的的修改

Java代碼
  1. class  PublicationRepository {  
  2.   public   void  save(Publication pub) {  
  3.    if  (hasSameName(pub)) {  
  4.      throw   new  InvalidPublicationException();  
  5.    }  
  6.    dao.save(pub);  
  7.   }  
  8. }  
Java代碼 
  1. class PublicationRepository {  
  2.   public void save(Publication pub) {  
  3.    if (hasSameName(pub)) {  
  4.      throw new InvalidPublicationException();  
  5.    }  
  6.    dao.save(pub);  
  7.   }  
  8. }  


另外,Repository只應(yīng)該負責(zé)Aggregate Root。對于被Aggregate的對象,應(yīng)該用Navigation,也就是在關(guān)系之間游走來獲取。所以不是所有的查詢都必須由Repository來完成,比如說:

Java代碼
  1. class  Contact {  
  2.   private  List<ContactNote> contactNotes =  new  ArrayList<ContactNote>();  
  3.   public   void  contactedBy(User accountManager, DateTime time){  
  4.     ContactNote contactNote = new  ContactNote( this , accountManager, time);  
  5.     if  (isDuplicated(contactNote)) {  
  6.       throw   new  InvalidContactNote();  
  7.     }  
  8.     contactNotes.add(contactNote);  
  9.   }  
  10.   private   boolean  isDuplicated(ContactNote contactNote) {  
  11.     // 查詢contactNotes   
  12.     return  xxx;  
  13.   }  
  14. }  
Java代碼 
  1. class Contact {  
  2.   private List<ContactNote> contactNotes = new ArrayList<ContactNote>();  
  3.   public void contactedBy(User accountManager, DateTime time){  
  4.     ContactNote contactNote = new ContactNote(this, accountManager, time);  
  5.     if (isDuplicated(contactNote)) {  
  6.       throw new InvalidContactNote();  
  7.     }  
  8.     contactNotes.add(contactNote);  
  9.   }  
  10.   private boolean isDuplicated(ContactNote contactNote) {  
  11.     // 查詢contactNotes  
  12.     return xxx;  
  13.   }  
  14. }  


現(xiàn)狀是,對象之間的關(guān)聯(lián)不可查詢導(dǎo)致了,很多這樣的查詢必須通過xxxDao,xxxRepository來完成。其實它們都不應(yīng)該插手。 

理想情況下,只有業(yè)務(wù)開始的時候用repository加載對象,在結(jié)束的時候用repository把對象存儲回去,中間都是領(lǐng)域?qū)ο笤诨ハ嘧?用。而DAO,可以以Generic Query Builder的形式存在,不過和它之前被發(fā)明出來的意圖已經(jīng)不是一個東西了。

 

DAO原本的作用就是隔離數(shù)據(jù)庫的影響,沒有業(yè)務(wù)邏輯。而Repository更抽象,從概念上來說是一個可以全局訪問的集合,從這個意義上來講對你所舉 PublicationRepository,使用add(Publication pub)作為方法簽名要更好一些。Repository也負責(zé)保持完整對象的完整性,PublicationRepository的例子也說明了這一點, 另外一個例子,但從數(shù)據(jù)庫重建一個對象時,由于外部原因,對象已經(jīng)變得不完整,將它恢復(fù)為一個完整的對象或者直接拋異常也是Repostory的責(zé)任,它 可以將這種保證對象完整性的責(zé)任委托給別的對象(如Factory)。將Repository和DAO聯(lián)合起來用應(yīng)該很有用,謝謝你的提醒! 


至于

引用
Java代碼
  1. class  Contact {  
  2.   private  List<ContactNote> contactNotes =  new  ArrayList<ContactNote>();  
  3.   public   void  contactedBy(User accountManager, DateTime time){  
  4.     ContactNote contactNote = new  ContactNote( this , accountManager, time);  
  5.     if  (isDuplicated(contactNote)) {  
  6.       throw   new  InvalidContactNote();  
  7.     }  
  8.     contactNotes.add(contactNote);  
  9.   }  
  10.   private   boolean  isDuplicated(ContactNote contactNote) {  
  11.     // 查詢contactNotes   
  12.     return  xxx;  
  13.   }  
  14. }  

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多