我覺得有不少人被Spring帶著跑偏了!
前幾天我在GitHub上看到了一個在線商城的項目,star數上萬,我很好奇,這個項目這么多人捧場,應該不錯吧!于是把代碼下載下來,瀏覽了一番。
讓我驚訝的是,代碼寫得有點慘,長達幾百行的方法比比皆是。沒有面向對象編程的思維,用過程性的方式把業(yè)務邏輯統(tǒng)統(tǒng)堆砌在Service, 沒有領域模型,沒有職責的劃分。
我不清楚為什么這個項目能獲得這么高的star數,如果大家真的覺得這個項目質量好,那就太可怕了。我更傾向于相信很多人覺得這個項目有用,改一改就能當畢業(yè)論文,或者當簡歷中的項目經驗吧。
1
這不是我今天想說的重點,回想起我看過的開源項目和經歷過的商業(yè)項目,有個問題挺突出的:
這些代碼中只有Controller, Service ,DAO,并且用過程性的方式把業(yè)務邏輯像垃圾一樣丟到Service層當中,讓Service層變得臃腫不堪,難以維護。
在這些代碼中的領域模型被稱為“貧血模型”, 特點是只有getter/setter方法,Service層經常做的事情就是:
1. 根據某個ID從數據庫獲取貧血的對象
2. 創(chuàng)建一個新的貧血對象,執(zhí)行一堆業(yè)務邏輯,調用setter方法裝配這些貧血的對象,然后保存到數據庫中。
再加上修改和刪除,那就是著名的CRUD了。
貧血的領域模型在這里主要承擔的是數據容器和數據傳輸的職責,而不是計算業(yè)務邏輯的地方。
關于貧血模型,充血模型, 15年前在javaeye網站上就有過非常充分的討論(參見文末的鏈接),沒看過的強烈建議讀一讀。
軟件開發(fā)大牛Martin Fowler在18年前也出版過一本書,叫做《企業(yè)應用架構模式》,其中講到的“事務腳本”就是這種方法。
沒想到這么多年過去了,還是有很多Spring項目在使用事務腳本/貧血模型!
當然,沒有東西絕對的,事務腳本/貧血模型也有它適用的地方,那就是對于簡單的,沒有復雜業(yè)務的系統(tǒng),大部分都是CRUD, 那可以快速地把系統(tǒng)給搞起來。
但是業(yè)務邏輯一旦變復雜,Service層就難以承受,變得難以閱讀,難以修改,難以維護了。 這時候一定要做出重構,給Service層減負,讓業(yè)務邏輯呆在它應該呆的地方,即領域模型當中。
2
我在標題中說被Spring帶著跑偏了,好像是把鍋甩給了Spring,冤枉Spring了,管人家Spring什么事?明明是程序員沒有用好嘛!
在網上有一種說法:Spring的作者Rod Johnson也承認,Spring也是在沿襲EJB2時代的“事務腳本”,也就是面向過程編程 !
雖然我沒有找到這句話的原始出處,但是不得不說SSH/SSM對很多初學者的誤導作用,在SSH/SSM的很多例子當中,用的都是這種貧血模型,這就給初學者帶來了一種印象:把貧血的領域對象當作C語言的結構體,把Service當作C語言中的函數來使用,似乎這就是正確的用法。
Martin Fowler提出了貧血模型這一反模式以后,Spring的核心開發(fā)人員Craig Walls(也是《Spring實戰(zhàn)》的作者)在2005年就寫過一篇文章《Spring 2.0 vs 貧血模型》,其中闡述了Spring 2.0如何避免貧血模型,不過他在其中提到的是領域模型自己處理持久化,然后需要引用DAO的問題。
3
很多人給我說工作瑣碎,修修補補,很沒意思。我建議看一看自己的項目,是不是符合這樣的條件:業(yè)務復雜+貧血模型,如果是的話就可以學習面向對象的設計,體會一下真正的領域模型。
如果你成功了,看到清爽的、薄薄的Service層,有著充分測試用例的Domain Object,心里會很爽的。
從執(zhí)行策略上來說,先要想辦法獲取領導支持,如果沒有領導首肯,自己私下里練習一下,然后把漂亮的結果展示給領導看看,更有說服力。
如果你面對這樣的代碼束手無策的時候,不妨看看這篇文章:《當15年的老司機遇到爛項目、爛代碼時,他會做這5件事!》
我知道做出這樣的改變是不容易的,但是這么做了才是體現你能力的時候,是讓你脫穎而出的時候。
參考文章:
Javaeye的討論:
https://www.iteye.com/topic/11712
Martin Fowler的文章:
https://www.ituring.com.cn/article/details/25
Craig Walls的文章:
https://martinfowler.com/bliki/AnemicDomainModel.html
【本文為51CTO專欄作者“劉欣”的原創(chuàng)稿件,轉載請通過作者微信公眾號coderising獲取授權】