作者:
江南白衣 因?yàn)镾pring自帶的sample離我們的實(shí)際項(xiàng)目很遠(yuǎn),所以官方一點(diǎn)的model層模式展現(xiàn)就靠Appfuse了。
但Appfuse的model層總共有一個(gè)DAO接口、一個(gè)DAOImpl類、一個(gè)Service接口、一個(gè)ServiceImpl類、一個(gè)DataObject.....大概只有受慣了虐待的人才會(huì)欣然接受吧。
另外,Domain-Driven逢初一、十五也會(huì)被拿出來討論一遍。
其實(shí)無論什么模式,都不過是一種人為的劃分、抽象和封裝。只要在團(tuán)隊(duì)里理解一致,自我感覺優(yōu)雅就行了。
我的建議是,一開始DO和Manager一生一旦包演全場(chǎng),DO作為純數(shù)據(jù)載體,而Manager類放置商業(yè)方法,用getHibernateTemplate()直接訪問數(shù)據(jù)庫(kù),不強(qiáng)制基于接口編程。當(dāng)某天系統(tǒng)復(fù)雜到你直覺上需要將DAO層和Service層分開時(shí),再分開就好了。
1.DataObject類
好聽點(diǎn)也可以叫Domain Object。Domain Driven Development雖然誘人,但因?yàn)镴ava下的ORM框架都是基于Data Mapper模式的,沒有Ruby On Rails中那種Active Recorder的模式。所以,還是壓下了這個(gè)欲望,Data Object純粹作一個(gè)數(shù)據(jù)載體,而把數(shù)據(jù)庫(kù)訪問與商業(yè)邏輯操作統(tǒng)一放到Manager類中。
2.Manager類 我的Manager類是Appfuse中DAO類與Service類的結(jié)合體,因?yàn)椋?BR>
2.1 不想使用純DAO 以往的DAO是為了透明不同數(shù)據(jù)庫(kù)間的差異,而現(xiàn)在Hibernate已經(jīng)做的很好。所以目前純DAO的更大作用是為了將來可以切換到別的ORM方案比如iBatis,但一個(gè)Pragmaic的程序員顯然不會(huì)無聊到為了這個(gè)機(jī)會(huì)不大的理由,現(xiàn)在就去做一個(gè)純DAO層,項(xiàng)目又不是Appfuse那樣為了demo各種ORM方案而存在。
2.2 也不想使用Service層來為Dao解耦
在JPetStore里有一個(gè)很薄的Service層,F(xiàn)ascade了一堆DAO類,把這些DAO類的所有方法都僵硬的重復(fù)了一遍。理論上一個(gè)Manager類可以管理數(shù)個(gè)Dao類,可以避免Dao之間直接耦合。但既然有Manager的情況下,商業(yè)邏輯都是寫在Manager類的,那樣子Manager似乎還是調(diào)用另一個(gè)Manager比較妥當(dāng),調(diào)用裸Dao可能存在忽略了某些邏輯。所以,耦合又從Dao層升到Service層了。
所以,除非你做的是超薄的不帶邏輯的Service層,否則沒有解耦的意義。
何況,對(duì)一個(gè)不是死搬書的Designer來說,組件邊界之內(nèi)的類之間的耦合并不是耦合。
3.去除不必要的基于接口編程
眾所周知,Spring是提倡基于接口編程的。
但有些Manager類,比如SaleOrderManager ,只有5%的機(jī)會(huì)再有另一個(gè)Impl實(shí)現(xiàn)。95%時(shí)間里這兩兄弟站一起,就像C++里的.h和.cpp,徒增維護(hù)的繁瑣(經(jīng)常要同步兩個(gè)文件的函數(shù)聲明),和代碼瀏覽跳轉(zhuǎn)時(shí)的不便(比如從Controler類跟蹤到Service類時(shí),只能跳轉(zhuǎn)到接口類的相應(yīng)函數(shù),還要再按一次復(fù)雜的熱鍵才跳轉(zhuǎn)到實(shí)現(xiàn)類)
連Martin Flower都說,強(qiáng)制每個(gè)類都分離接口和實(shí)現(xiàn)是過猶不及。只在有多個(gè)獨(dú)立實(shí)現(xiàn),或者需要消除對(duì)實(shí)現(xiàn)類的依賴時(shí),才需要分離接口。
3.1 DAO被強(qiáng)制用接口的原因 Spring IOC本身是不會(huì)強(qiáng)制基于接口的,但DAO類一般要使用Spring的聲明式事務(wù)機(jī)制,而聲明式的事務(wù)機(jī)制是使用Spring AOP來實(shí)現(xiàn)的。Spring AOP的實(shí)現(xiàn)機(jī)制包括動(dòng)態(tài)代理和Cgilib2,其中Spring AOP默認(rèn)使用的Java動(dòng)態(tài)代理是必須基于接口,所以就要求基于接口了。
3.2 解決方法 那就讓Spring AOP改用CGLib2,生成目標(biāo)類的子類吧,我們只要指定使用聲明式事務(wù)的FactoryBean使用CGLib的方式來實(shí)現(xiàn)AOP,就可以不基于接口編程了。
指定的方式為
設(shè)置proxyTargetClass為true。如下:
<bean class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
id="baseService" abstract="true">
<property name="transactionManager" ref="transactionManager"/>
<property name="proxyTargetClass" value="true"/>


</bean> 又因?yàn)檫@些Service Bean都是單例,效率應(yīng)該不受影響。
4.總結(jié)
對(duì)比Appfuse里面的5個(gè)類,我的Model層里只有VO作為純數(shù)據(jù)載體,Manager類放商業(yè)方法。有人說這樣太簡(jiǎn)單了,但一個(gè)應(yīng)用,要?jiǎng)澇蓭讉€(gè)JSP,一個(gè)Controller,一個(gè)Manager,一個(gè)VO,對(duì)我來說已經(jīng)足夠復(fù)雜,再要往上架墻疊屋,恕不奉陪,起碼在我的項(xiàng)目范圍里不需要。(但有很多項(xiàng)目是需要的,神佑世人)
后記:迫于世人的壓力,
SpringSide暫時(shí)還是把DAO和Service層分開了,但依然堅(jiān)持不搞那么多接口。
另外,盡量利用IDEA的代碼生成熱鍵,為Manager類生成delegate的Dao類方法。
相關(guān)文章
簡(jiǎn)化Spring(1)--配置文件 簡(jiǎn)化Spring(2)--Model層 簡(jiǎn)化Spring(3)--Controller層 簡(jiǎn)化Spring(4)--View層