DAO接口
為每個DAO聲明接口的好處在于:
- 可以在尚未實(shí)現(xiàn)具體DAO的時候編寫上層代碼,如Service里對DAO的調(diào)用
- 可以為DAO進(jìn)行多實(shí)現(xiàn),例如有JDBCDAO實(shí)現(xiàn),MyBatisDAO實(shí)現(xiàn),而不需要更改上層代碼,只需要簡單的在Spring的IoC配置里修改一下注入的DAO實(shí)現(xiàn)
Service接口
- 可以在尚未實(shí)現(xiàn)具體Service情況下編寫上層改代碼,如Controller對Service的調(diào)用
- Spring無論是AOP還是事務(wù)管理的實(shí)現(xiàn)都是基于動態(tài)代理的,而動態(tài)代理的實(shí)現(xiàn)依賴于接口,所以必須有接口的定義才能使用這些功能
- 可以對Service進(jìn)行多實(shí)現(xiàn)
總的來說,接口的優(yōu)勢就在于規(guī)范方法參數(shù),返回值,另外可以實(shí)現(xiàn)多態(tài),結(jié)合Spring來說接口對于使用Spring的各種功能也是不可或缺的
另外,使用接口對于測試代碼也是有好處的,對于mock一個方法來說,我們不需要關(guān)注方法的具體實(shí)現(xiàn),因?yàn)楸緛韒ock就會將方法內(nèi)部實(shí)現(xiàn)置空,我們的關(guān)注點(diǎn)集中于方法參數(shù)以及返回值,所以使用接口對于快速實(shí)現(xiàn)流程上的測試是有好處的.
使用接口是為了調(diào)用與實(shí)現(xiàn)解耦,帶來的好處是可以各干各的了,帶來的壞處是從一個概念變成了兩個概念,增加了系統(tǒng)的復(fù)雜度。衡量一下在具體場景中是弊大于利還是利大于弊,就可以做選擇了。當(dāng)然,在大部分場景下,還要考慮一個因素,就是你會不會寫接口。沒有良好接口設(shè)計(jì)能力的人,寫出來的接口抽象不合理,等于沒寫,什么好處都得不到,只有壞處,這種情況下干脆別寫。那怎么衡量你會不會寫接口呢,我的經(jīng)驗(yàn)是,至少見過一次寫了接口后得到明確好處的例子。
什么情況下需要各干各的?
最簡單的場景,寫接口的是你,寫實(shí)現(xiàn)的是你小弟。當(dāng)然大多數(shù)類似情況沒必要真的建一個interface然后再讓人家去implements,把函數(shù)的第一行寫好,注釋寫好,代碼提交上,里面的內(nèi)容讓小弟去填就行了。
另一種情況,調(diào)用代碼先于實(shí)現(xiàn)代碼編寫。比如你開發(fā)的是struts這種東西,那你指定得搞個Action接口。
再一種情況,多種業(yè)務(wù)的模式類似。此時這個接口類實(shí)際上相當(dāng)于某一層的抽象。定義出一個層后,有多種實(shí)現(xiàn),然后通過向調(diào)用端注入不同的實(shí)現(xiàn)類,實(shí)現(xiàn)不同的邏輯。如果這種注入不能在編譯期完成的話,也就需要用接口抽象一下。
上面這幾種情況寫得有點(diǎn)繞,沒辦法,太難表述了并且好多事我自己也沒想明白……
說到題目中的場景。
先說dao,這玩意兒是做數(shù)據(jù)庫讀寫的。對應(yīng)一下上面那幾種情況:你作為項(xiàng)目架構(gòu)師想寫兩行代碼就讓苦逼小弟加班干活然則自己去泡妹子的話,可能需要寫個interface里面幾個抽象的insert、delete之類的函數(shù);項(xiàng)目在快速原型階段如果客戶滿意就掏錢買oracle如果客戶不滿意就得免費(fèi)MySQL的話,你可能需要定義個dao接口然后先用內(nèi)存數(shù)據(jù)庫寫點(diǎn)能讓原型跑起來的實(shí)現(xiàn),等一切有定論了再說;每個類都有一個dao,每個dao都有crud基本方法的話你可能需要定義一個通用Dao接口然后搞點(diǎn)代碼技巧不用一個個的去寫體力代碼從此登上人生巔峰。所以dao接口還是有用的。
再說service,這玩意兒更得具體問題具體分析。不去摳理論的話,什么是service,我的理解就是一段段實(shí)現(xiàn)了某個邏輯的代碼的組合。所以service是個比dao更抽象的概念,嚴(yán)格來講dao就是一種service。只不過在java web開發(fā)中,dao是個人人都得寫的東西,所以都拿出來單說了。因此,后面說的service跟dao沒有本質(zhì)分別。
還是上面說的那幾種情況:
先從工序上說,你在寫上一層的時候,會用到下一層提供的邏輯,具體表現(xiàn)形式就是各種各樣的service類和里面的方法。上一層開搞的時候,一定會知道的一個事是下一層會干什么事,比如“將傳入編號對應(yīng)的人員信息設(shè)置為離職”,但下一層的代碼不一定已經(jīng)一行一行寫出來了。所以這會兒需要有個接口,讓寫上層代碼的人先能把代碼寫下去。有各種理由可以支持這種工序的合理性,比如一般來說,上一層的一行代碼會對應(yīng)下一層的好多行代碼,那先讓寫上層代碼的人寫一遍,解決高端層面的bug,會提高很多效率。
再從抽象角度說,不同業(yè)務(wù)模塊之間的共用,不一定是共用某段代碼,也可能是共用某段邏輯框架,這時候就需要抽象一個接口層出來,再通過不同的注入邏輯實(shí)現(xiàn)。比如模塊1是登記學(xué)生信息,模塊2是新聞發(fā)布,看上去風(fēng)馬牛不相及。但分析下來如果兩個模塊都有共同點(diǎn),順序都是1、驗(yàn)證是否有權(quán)限 2、驗(yàn)證輸入?yún)?shù)是否合法 3、將輸入?yún)?shù)轉(zhuǎn)化為業(yè)務(wù)數(shù)據(jù) 4、數(shù)據(jù)庫存取 5、寫log,那就可以寫一個service接口,里面有上述5個函數(shù),再分別寫兩個service實(shí)現(xiàn)。具體執(zhí)行的時候,通過各種注入方法,直接new也好,用spring注入也好,實(shí)現(xiàn)不同的效果。
當(dāng)然上面說的這種情況很少有人這么干,因?yàn)橐呀?jīng)普遍到這個程度,再精化精化就是struts了,java web的各種mvc框架都提供機(jī)制給你干這個事。但是每個項(xiàng)目或產(chǎn)品,都應(yīng)該可以用類似的思路抽象出一些東西,如果抽象合理,會很大程度的提高項(xiàng)目架構(gòu)的合理性。
這些要是能搞定,那什么寫個接口然后實(shí)現(xiàn)個mock用于測試這種事,信手拈來。
JavaWeb 開發(fā)中,服務(wù)器端通常分為表示層、業(yè)務(wù)層、持久層,這就是所謂的三層架構(gòu)。三層架構(gòu)的每一層都有自己的開發(fā)模式,即架構(gòu)模式,如下圖。
其中,表示層一般是采用 MVC 架構(gòu)模式,業(yè)務(wù)層有事務(wù)腳本模式、領(lǐng)域模型模式等,持久層有數(shù)據(jù)映射器模式(Hibernate即是典型的)、入口模式(iBatis、JDBC)。企業(yè)應(yīng)用中最關(guān)鍵的顯然是業(yè)務(wù)層。而對于初學(xué)者來說,事務(wù)腳本模式是最為簡單,最容易掌握的。如果開發(fā)團(tuán)隊(duì)面向?qū)ο笤O(shè)計(jì)能力一般,而且業(yè)務(wù)邏輯相對簡單,業(yè)務(wù)層一般都會采用事務(wù)腳本模式。為嘛?簡單呀,是個人都能很快學(xué)會!(當(dāng)然,如果業(yè)務(wù)邏輯復(fù)雜,用事務(wù)腳本模式就很不明智了。嗯,簡單點(diǎn)講,就是違背了單一職責(zé)設(shè)計(jì)原則,Service類成為萬能的上帝,承擔(dān)了太多職責(zé)。。。)
那么什么是事務(wù)腳本模式呢?
所謂事務(wù),就是表示層的一個請求;所謂腳本就是一個方法或者一個函數(shù);所謂事務(wù)腳本就是將一次請求封裝為一個方法或者一個函數(shù)。所謂事務(wù)腳本模式,就是將業(yè)務(wù)層的對象分為三類,按照下圖的方式組織起來(下圖是用EA畫的UML類圖,一個簡單的學(xué)生管理的業(yè)務(wù)層設(shè)計(jì))。
如上圖所示,在事務(wù)腳本模式中,有三類對象。其中,Service類封裝業(yè)務(wù)流程(或者說是界面上的業(yè)務(wù)流程),DAO類封裝對持久層的訪問,DTO類封裝業(yè)務(wù)實(shí)體對象。各個對象之間的關(guān)系如上圖所示,這就是所謂業(yè)務(wù)邏輯的組織方式。
為什么要用Service接口和DAO接口?我們還得回到最基本的面向?qū)ο笤O(shè)計(jì)原則上去。
面向?qū)ο笤O(shè)計(jì)原則中有三條與此相關(guān):開閉原則、依賴倒轉(zhuǎn)原則、理氏替換原則。還記得依賴倒轉(zhuǎn)原則吧?高層不依賴于低層,二者都依賴于抽象,也就是面向接口編程。
為什么要用Service接口?是讓表示層不依賴于業(yè)務(wù)層的具體實(shí)現(xiàn)。為什么要用DAO接口?是讓業(yè)務(wù)層不依賴于持久層的具體實(shí)現(xiàn)。有了這兩個接口,Spring IOC容器才能發(fā)揮作用。
舉個例子,用DAO接口,那么持久層用Hibernate,還是用iBatis,還是 JDBC,隨時可以替換,不用修改業(yè)務(wù)層Service類的代碼。
使用接口的意義就在此。