2010-02-25 60 views
26

在我們的傳統Java EE應用程序中,有大量值對象(VO)類,它們通常只包含getter和setter,可能是equals()hashCode()。這些(通常)是要保存在持久性存儲中的實體。 (爲了記錄,我們的應用程序沒有EJB - 雖然可能會在將來更改),並且我們使用Hibernate來維護我們的實體。)用於操作VOs中數據的所有業務邏輯都位於不同的類(不是EJB,只是POJO)。我的OO思想討厭這個問題,因爲我確實相信某個班的操作應該在同一個班上。所以我有一種衝動,想要將邏輯轉移到相關的VO中。企業Java實體應該愚蠢嗎?

我剛剛和一位在Java EE方面比我有更多經驗的同事討論過,並且他證實了愚蠢的實體至少是過去推薦的方式。但是,他最近也讀過了對這種立場的有效性提出質疑的意見。

據我所知,有至少限制了什麼可以是實體類內部把問題:

  • 它不應該有直接依賴於數據層(例如查詢代碼而應進入單獨的DAO)
  • 如果是直接暴露於較高的層或客戶端(例如,經由SOAP),它的界面可能需要被限制

是否有任何更多的有效理由移動邏輯INT我的實體?還是考慮其他問題?

+1

實體是VO嗎? – 2010-02-25 11:06:43

+0

@Pascal是的 - 請參閱我的更新。 – 2010-02-25 11:18:59

回答

17

DTOVO應該用於傳輸數據並且不嵌入邏輯。另一方面,業務對象應該嵌入一些邏輯。我說一些,因爲總是有一個平衡點,可以找到你在服務中放置什麼,這些服務協調涉及多個業務對象的邏輯和你在業務對象本身中放置的內容。業務對象中的典型邏輯可以是驗證,字段計算或其他一次隻影響一個業務對象的操作。

請注意,到目前爲止,我還沒有提到實體。持久實體通過ORM推廣,我們現在試圖同時使用持久實體作爲DTO 業務對象。也就是說,實體本身在層和層之間流動,幷包含一些邏輯。

是否有更多有效的理由不是 將邏輯移動到我的實體?還是要考慮其他問題?

正如您所指出的那樣,這都是依賴關係和您所暴露的問題。只要實體是愚蠢的(靠近DTO),它們就可以很容易地被隔離在一個專用的罐子裏,作爲的API。你在實體中放置的邏輯越多,它就越難做到這一點。注意你所暴露的東西以及你依賴的東西(負載類,客戶端也需要有依賴類)。這適用於例外情況,繼承層次結構等。

僅舉一個例子,我有一個項目,其中實體在業務層中使用方法toXml(...)。因此,實體的客戶端依賴於XML。但如果你不太關心圖層,並嚴格區分API和實現,我認爲在實體中移動一些邏輯是很好的。

編輯

這個問題已經討論了很多的時間和很可能會繼續,因爲沒有明確的答案進行討論。一些有趣的環節:

+0

我發現大多數有用的答案,很難挑選一個「最好的」。然而,我發現你是最平衡和最有洞察力的,所以你得到了複選標記:-)謝謝。 – 2010-03-01 21:37:08

+0

是的,經過一年多的重新閱讀,我不得不同意ewernli肯定知道他在這個話題上的方法。 – 2011-08-09 21:18:06

8

我認爲你的觀點是有效的。
查看更多 - http://martinfowler.com/bliki/AnemicDomainModel.html
使用JPA,實體是輕量級對象。所以我不認爲它有任何問題有邏輯。

在使用SOAP/Web服務的情況下,我會添加一個單獨的Facade層。

+3

同意。使用只有getter和setter的對象完全不符合OO,更像是在C中使用結構。我喜歡C,但這是Java,我每次必須涉及一個類,其中不包含任何其他內容,而是表單中的樣板代碼getX()和setX() – 2010-02-25 11:43:13

+0

@Padmarag優秀的文章,謝謝:-) – 2010-02-25 12:28:21

+1

我希望它是這麼簡單:) – ewernli 2010-02-25 12:32:13

1

您所指的慣例是採用貧血域模型,而不是豐富的域模型,其中實體是簡單的POJO註釋爲bean(@Entity),在獲取者和設置者方面只有最小值。所以具有toXML()方法的類將被視爲富域。我想這有助於保持清晰的視圖,即使粒度不同,映射到關係數據庫的內容也是如此。

其優點是邏輯和數據之間有明確的分離(這是OO哲學被破壞的地方)。方法是將這些分組到業務邏輯類或服務中。這是根據分層架構,其中各個層是域,服務,DAO和UI。

這就是理論。

編輯:只是爲了澄清,當我說邏輯和數據之間存在明顯的分離時,我的意思是一個對象是數據導向的,另一個是方法導向的方式,可以認爲是程序的失效。

+2

@James P.如果你認爲這是因爲這個「破碎」,我恐怕你不太明白OO的觀點:-( – 2010-02-25 14:06:54

+1

這是可能的,我總是開放的啓蒙;)。糾正我,如果我錯了,但聽起來像封裝原則是在這裏辯論的中心。 – 2010-02-25 15:19:11

+0

P.我不知道我明白你的目標。無論如何,我不想就OO進行「辯論」,因爲a)我寧願將這個話題放在焦點上,b)關於OO的觀點是(主觀的)主觀的和與上下文相關的,c)600個字符不足以滿足這個要求無論如何。 – 2010-02-25 21:58:42

1

的主p爲這些類添加邏輯的問題是,它們需要更多的屬性來跟蹤對象狀態,而這些額外的屬性通常不需要序列化。這意味着這些類的序列化機制需要額外的工作。

考慮到許多項目都混合了jr。程序員和sr。程序員和大部分工作是由不瞭解(或關心)最優序列化的jr執行的,將這些普通的舊java對象作爲「值對象」很容易,它們幾乎只是傳遞和接收數據,把邏輯放在其他地方。

如果您設法將邏輯放置在業務對象(即VO +邏輯)中,我認爲這樣做會更好。只要記住整個團隊是在同一頁面,他們不重複的代碼和邏輯(這從來沒有發生的權利?)

簡短的回答:不,他們不應該總是愚蠢的,但是更容易有他們這樣的方式。

+0

如果序列化存在問題,可以使其他屬性爲瞬態。沒什麼大不了的。 – 2010-02-25 17:32:09

+1

@Matthew當然,只要新手明白你在說什麼。 – OscarRyz 2010-02-26 14:32:01

4

除了前面提到的Fowler文章之外,埃裏克埃文斯的書域驅動設計(2004)有一篇豐富與貧血域模型的完整論文。

此外,檢查出http://dddcommunity.org/

+1

你真的需要閱讀這本書。這是工作既具有開創性又有用的罕見案例之一。 – HDave 2013-10-31 12:45:59

3

右鍵,下面是我從我的Java EE教練得到了反饋的摘要。

從實踐的角度來看,只要這些方法與實體的屬性一起工作,通過移動邏輯在貧血和富域之間達成妥協就不應成爲問題。當涉及到一個豐富的領域時,這條線必須在某個地方繪製出來,顯然福勒和國王都已經向這個方向發表了評論。

例如,考慮BankAccount中的一個calculateInterestRate()方法,該方法從其他域對象中提取信息,例如驗證某人已經是客戶端的時間。爲了避免依賴,可以將對象分割爲多個對象,但這種方式意味着代碼可能會結束被多個類分散。在這一點上,人們不妨做一個InterestCalculator類。

要考慮的另一件事是線程安全。由Spring處理的Singleton DAO和服務應該是線程安全的,而域模型中的任何東西都會暴露給併發問題。

最後,還有維護問題。你確定你會在幾年的時間內維護應用程序嗎?您所做的選擇似乎是合理的,但是您是否確信下一個開發人員將具備必要的專業知識,以便輕鬆理解您的代碼?

0

我已經做了一些C編程,雖然OOP對於通過構建類層次結構來簡化問題很好,但我仍然發現簡單的C方法在很多情況下是最簡單和最好的。在C中,我們只與公共成員結合,程序員將這些結構傳遞給函數(在Java中,這樣的函數將是例如某些實用程序類中的靜態函數),該函數操作成員。數據和算法是分開的,因爲函數不是結構的成員。我一直有這樣的感覺,即VO對象就像C中的結構體一樣。當C語言不是最大的時候,有很多情況,例如,沒有層次結構,沒有多態性,體面的OOP程序員覺得有用。然而,一般來說,我喜歡這種簡單的C方法,並且更喜歡使用它,除非我知道OOP技術會非常有益。例如。當我需要使用類層次結構來建模時,或者需要確保一個或多個類(層次結構中)的成員始終保持一致時,我不能使用C結構方法。但在這些情況下,我不僅會有制定者和獲得者。

我還指這篇文章是關於C++,但我喜歡這傢伙怎麼解釋這樣的事情: http://www.gotw.ca/gotw/084.htm 這篇文章有什麼時候做一個函數的類成員2個規則:

(從下面我留下了一些東西的報價,看過原著,如果你想看到所有)

  1. 務必使它的成員,如果它是一個:哪些操作必須是會員,因爲C++語言只是這麼說的(例如,c onstructors)還是由於功能原因(例如,它們必須是虛擬的)?如果他們必須是,那麼哦,他們必須是;案件關閉。

  2. 如果需要訪問內部部件,則需要使其成爲會員:哪些操作需要訪問我們必須通過友誼授予的內部數據?這些通常應該是成員。

在所有其他情況下,寧願使它成爲非成員非友人:哪些操作可以與非會員非友人一樣工作?這些可以並因此通常應該是非成員。這應該是爭取的默認情況。

我有這樣的感覺,如果您不確定是否向這些類添加函數,那麼您並不需要這麼做。我在這裏寫的僅僅是爲什麼我不會爲這些類添加方法的所有原因的一部分,但是也許這是我所有其他原因的父親。但這是YMMV所有主觀的。順便提一句,靜態效用函數的方法使單元測試在大多數情況下都很簡單

0

實體頻繁生成。在像Java這樣的語言中,不存在部分類的事情,所以我們不能擴展一個實體(不考慮像Visitor這樣的模式)。因此,當您需要重新生成實體時,必須再次添加業務邏輯,這實際上並不實際。

對於實體我寧願去實體和邏輯之間的設計high cohesion

要考慮的其他問題是事實並非所有情況下實體的所有業務操作都是平等的。另外,允許實體中的業務邏輯傾向於使實體與XML等集成技術耦合,在我看來,這些技術應始終遠離域邏輯。在洋蔥體系結構中,XML將位於內部的外殼和域對象中,以說明如果要創建可重複使用的可插拔系統,它們實際上相互之間的距離有多遠。