Java 中 dao 層和 service 層都使用接口,是不是爲使用接口而使用接口?java
我的認爲,若是沒有搞懂爲何用接口,那麼有些人就會逢類就要實現接口……在一些業務不復雜的場景下,真的沒有必要這樣作,可是內心要明白。程序員
前面都說了不少例子和理論了,不過學習就是站在巨人的肩膀上,不斷重複,概括和昇華到方法論的過程,再重複一下,引用軟件工程文獻:spring
使用接口是爲了把調用與實現解耦,帶來的好處是能夠各幹各的,帶來的壞處是從一個概念變成了兩個概念,增長了系統的複雜度。數據庫
衡量一下在具體場景中是弊大於利仍是利大於弊,就能夠作選擇了。編程
固然,在大部分場景下還要考慮一個因素,就是你會不會寫接口。沒有良好接口設計能力的人,寫出來的接口抽象不合理,等於沒寫,什麼好處都得不到,只有壞處,這種狀況下乾脆別寫。安全
那怎麼衡量你會不會寫接口呢,個人經驗是,至少見過一次寫了接口後獲得明確好處的例子。架構
最簡單的場景,寫接口的是 A,寫實現的是 B。固然大多數相似狀況不必真的建一個 interface,而後再讓別人去 implement。mvc
另外一種狀況,調用代碼先於實現代碼編寫。好比 A 開發的是 struts2 這種框架,那 A 先得搞個 Action 接口,讓程序先跑起來。oracle
dao 作數據庫讀寫用的。對應上面那幾種狀況:框架
一、做爲架構師想寫兩行代碼就讓小弟加班幹活然則本身去泡妹子的話,可能須要寫個 interface ,裏面幾個抽象的 insert、delete 之類的方法;
二、項目在快速原型階段若是客戶滿意就掏錢買oracle,若是客戶不滿意就用免費MySQL的話,你可能須要定義個 dao 接口而後先用內存數據庫寫點能讓原型跑起來的實現,等一切有定論了再說;
三、每一個類都有一個dao,每一個 dao 都有 crud 方法的話,你可能須要定義一個通用 Dao 接口,而後搞點代碼,不用一個個的去寫體力代碼今後登上人生巔峯。因此dao接口仍是有用的。
service 做爲業務邏輯的實現,得具體問題具體分析:
不去摳理論的話,什麼是 service——就是一段實現了某個邏輯的代碼組合。因此 service 是比 dao 更抽象的概念,嚴格來說 dao 就是一種 service。只不過在 java 開發中,dao 是我的人都得寫的東西,因此都拿出來單說。所以,service 跟 dao 沒有本質分別。
寫上一層的時候,會用到下一層提供的邏輯,具體表現形式就是各類各樣的 service 類和裏面的方法。上一層開始的時候,必定會知道下一層會幹什麼事,好比 「將傳入編號對應的人員信息設置爲離職」,但下一層的代碼不必定已經實現好。因此須要有個接口,讓寫上層代碼的人先能把代碼寫下去。有各類理由能夠支持這種工序的合理性,好比通常來講,上一層的一行代碼會對應下一層的好多行代碼,那先讓寫上層代碼的人寫一遍,解決高端層面的bug,會提升不少效率。
不一樣業務模塊之間的共用,不必定是共用某段代碼,也多是共用某段邏輯,這時候就須要抽象一個接口層出來,再經過不一樣的注入邏輯實現。
好比模塊1是登記學生信息,模塊2是新聞發佈,看上去風馬牛不相及。但分析下來若是兩個模塊都有共同點,順序都是;
一、驗證是否有權限
二、驗證輸入參數是否合法
三、將輸入參數轉化爲業務數據
四、數據庫存取
那就能夠寫一個 service 接口,裏面有上述 5 個方法,再分別寫兩個 service 實現。具體執行的時候,經過各類注入方法,直接 new 也好,用 spring 注入也好,實現不一樣的效果。
java 的各類 mvc 框架都提供機制給你幹這個事。每一個項目或產品,都應該能夠用相似的思路抽象出一些東西,若是抽象合理,會很大程度的提升項目架構的合理性。
這些搞定,寫個接口而後實現 mock 用於單元測試這種事,信手拈來。
說實話,總結到這裏,都是以前的種種的疑問的解答和對概念理解的昇華,也是爲了複習用,可是說來講去就是那些東西,高內聚,低耦合,開閉原則,單一職責原則,面向接口編程原則,業務和數據分離,工做內容分離,解耦,封裝,多態,代碼隱藏……其實就是反覆這些東西的舉例,早已經在十幾年前就讓前輩和大牛們玩爛的東西……
剛開始閱讀《Thinging in Java》一書,有以下的說法:
接口與具體實現分離
這裏的具體實現如何理解,這裏仍是要聯繫到接口的重要使用目的之一:向上轉型。
利用接口能夠被多個類去實現的特徵來分離工做內容,分離不一樣的業務邏輯而去靈活的插拔不一樣的但能夠替換的實現方法。
例如,有不一樣的動物,叫聲不同,只須要定義一個」叫聲(xxx)「方法,而讓牛、羊、青蛙等等去具體實現這個「叫聲(xxx)」方法,調用的時候只須要:
動物.叫聲(xxx);
就能發出對應動物的叫聲了,還要聯繫接口的一個重要使用目的之二:提供行爲的統一的約束,避免實例化,也就是說和具體實現要分離。
在這裏再簡單重複一些內容:接口每每定義的是一些行爲,在設計原則裏面有一條「單一職責「的原則,接口的做用只是提供一些方法給你,它不關心你是怎麼使用的。就像電腦的 USB 接口,咱們不須要關心這個 USB 接口是怎麼實現的,咱們只需可以使用這個USB接口。
客戶端不等於客戶端程序員,可見性也不是針對程序員的,其實不少問題,站在計算機的角度去看就一目瞭然了。
客戶端是指調用它的類或者具體對象,例如私有域不對具體的對象暴露,封裝可以保證外部的對象或者實例不能修改它,從而保證了類的安全。
封裝的做用不是真的把全部代碼實現都讓客戶端程序員看不到,這個隱藏的目的是讓客戶端在調用方法等行爲時可以按照編寫 API 的程序員制定的規則來:一個變量不能讓人家隨便修改,也不能隨便就能得到值,賦值要經過 setter,得到值需 getter 等方法,變量相應的就要經過 private 來隱藏。
舉一個形象的例子,形容程序員給用戶留了誤操做的坑,用來描述因封裝不當、沒有對類作好隱藏而致使 API 使用問題也是能夠的。
假設有一個自行車類,它有兩個成員變量——輪子(wheel)和踏板(pedal),它但願客戶端能調用『踩踏板』這個方法前進,也能調用『換輪子』這個方法維修自行車,但決不能讓用戶在『踩踏板』的同時『換輪子』,甚至像圖上那樣把棍子插輪子的縫隙裏,那麼就須要把輪子變量設爲 private——隱藏起來,不讓用戶自由獲取,得按照你的規則來。
好比騎車的時候就只返回 null,中止的時候就能夠返回正常——讓用戶能夠作換輪子之類的工做。
setter 的做用也是相似的,都是爲了保證客戶端調用時可以遵循 API 設計者的規則,不然調用時就會出現各類不可控的亂象,輕則讓調用者罵『這是哪一個 SB 設計的 API,代碼寫起來太難管理了』,重則會出現不少意想不到的 bug,也許實際上的封裝應該是對輪子再作進一步的封裝,那麼就要用內部類的方式。
總之看書的時候對有些想好久也不能明白的東西不用太鑽牛角尖,先照着書上說的作,等寫了足夠的代碼,經驗多一點了,天然就能理解一些東西爲何要那樣作了。