這個問題是一個非常好的問題,很多程序員在剛開始工作的時候,接觸到的項目都是這樣做的,一個接口對應一個實現類,然后就一直保持了這個習慣,但是可能并沒有考慮過為什么要這么做,或者并沒有想過這么做的好處是什么。
從工程化的角度來看,面向接口的編程是很有必要的,不過我們還是要結合實際情況來考慮。
“如果實現類可能會變化,那么最好使用面向接口編程,讓每一層代碼解耦,減少后期的修改工作量?!钡笃谡娴臅兓矗?/h1>
這句話看起來是沒有問題的,比如我們要實現一個給用戶發送短信的功能:
項目剛開始的時候,公司買了阿里云的服務,直接調用阿里云的一個接口進行短信發送;項目運行一段時間,又改成和騰訊云合作,那么發送短信的代碼就需要修改;為了避免領導說“再改回去吧”,這里寫一個接口、兩個實現,上層方法只面向接口編寫,就會方便很多。
不過,這一切都是我們“假想”出來的,至少我工作十多年,幾乎沒見過這種變來變去的需求,至于 Dao 層都加接口,避免項目做數據庫遷移,原來用 Oracle 現在要換成 MySQL 之類的... 從來沒有遇到過。

“接口相當于一個標準、一個規范,制定接口的人和實現接口的人,可能不是同一撥人。”如果是在同一個項目中呢?
這句話沒有錯,通常這種情況確實適合使用面向接口的編程,比如 JDBC 的實現:
這時候接口制定、接口實現和接口使用就是不同的人;
但是如果在同一個項目中呢?
如果項目是一個人開發的,那不用說,接口制定、實現和使用都是一個人,這時候還費這勁兒干啥;
我們大多數項目,接口類和實現類都是一個人來做的,很少有項目是領導把接口寫好,然后說“各位小弟,實現類就看你們的了”;
那么實現和使用的人呢?大部分時候也是同一個人,但也有例外,比如一個需求設計兩個功能模塊的修改,A 模塊的開發要用 B 模塊的一個功能,這時候可以 B 模塊的開發把接口先定好,這樣 A 和 B 就可以同時開發了;
當然 B 模塊的開發也可以先寫一個空方法:方法名稱、入參和出參都制定好,中間沒有邏輯,然后先提交一版,這樣 A 模塊的開發就可以快樂地 new() 了;不過我不太建議這樣做的原因是:通常開發 commit 一次,提交的內容應該是完整的。

那么既然我們的項目中,接口和實現類通常是一一對應的,而且開發的人也是同一個人,又沒有更換實現類的可能,那么還有必要面向接口編程么?
可以從以下幾方面考慮:
如果你的業務需要通過 RPC 的方式暴露給其他系統調用,這時候接口是有必要的,調用方只關注接口,不關心你的實現類,也不會因為你的實現類變更而受到影響;
測試驅動開發,在具體的實現類完成之前,需要先根據結構編寫測試用例;
現在有些框架只需要開發人員寫結構了,比如 Spring Data JPA、Feign 等等;
除了以上種種必須或最好使用接口的場景,我認為只要項目稍具規模,最好還是面向接口開發,就算一個接口對應一個實現類;當你帶領一個團隊的時候,可能這個團隊并不大,只有三五個開發人員,但是隨著項目的推進迭代,如果沒有使用面向接口開發的話,你會驚奇地發現,項目的代碼越來越復雜、越來越亂了,一個類有幾十個方法,一個方法有幾百行代碼,新來的組員看的一頭霧水,甚至你這個項目經理都不想看其中的代碼;
不要提什么制定代碼規范,規范一直都在,但是總有人不遵守,防是防不住的;
如果項目的 Service 層和 Dao 層,都是接口-實現類這樣做的,大多數時候代碼還是可以看的,相當于多了一個方法目錄,比如可以讓開發人員在寫一個新方法之前,看看能不能復用之前的方法;
當然,你不這么做的話,開發人員也可以直接在實現類中看現有的方法,只是相對來說說,在一個幾十上百行的目錄中翻找,和在一個成千上萬行的實現類的實現類中翻找(盡管有各種快捷的方法羅列出方法列表),相比還是前者更容易些。

我將持續分享Java開發、架構設計、程序員職業發展等方面的見解,希望能得到你的關注。
