Java基礎之教你如何正確運用依賴註入
一、C++的詬病
C++最遭人詬病的地方就是定義一個類需要寫兩個文件,一個.h文件和一個.cpp文件。例如定義一個CMainFrame類,mainframe.h內容如下:
class CMainFrame : public CFrameWndEx { protected: CMainFrame(); public: virtual ~CMainFrame(); };
mainframe.cpp內容如下:
CMainFrame::CMainFrame() { } CMainFrame::~CMainFrame() { }
當需要給這個類添加一個方法時,需要同時修改.h文件和.cpp文件。例如新增一個DefWindowProc函數。需要在.h文件中增加該函數的聲明。
class CMainFrame : public CFrameWndEx { protected: CMainFrame(); public: virtual ~CMainFrame(); protected: virtual LRESULT DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam); };
mainframe.cpp中增加DefWindowProc的定義:
LRESULT CMainFrame::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) { if(message==WM_NCPAINT ) { if(bShow){ ShowWindow(SW_SHOW); } else { ShowWindow(SW_HIDE); } } return CFrameWndEx::DefWindowProc(message, wParam, lParam); }
可以看出C++的類定義代碼中,一次變化需要修改兩個文件,其維護的繁瑣令人詬病。
二、Java的改進
然而Java的出現徹底解決瞭這個問題,一個類就對應一個.java文件(包括後來其他面向對象語言也秉承瞭這個思路,比如C#)。
比如一個LogService類用於對日志進行維護,起初隻包含日志的增刪功能,LogService.java代碼如下。
public class LogService{ public ServiceResult<Boolean> addLog (SysLogInfo logInfo) { ...... } public ServiceResult<Boolean> delLog (String id) { ...... } }
當需要增加一個updateLog方法時,僅需對LogService.java進行修改。
public class LogService{ public ServiceResult<Boolean> addLog (SysLogInfo logInfo) { ...... } public ServiceResult<Boolean> delLog(String id) { ...... } public ServiceResult<Boolean> updateLog (SysLogInfo logInfo) { ...... } }
一切變得方便瞭很多。
三、誤用導致的退步
但是最近在看一些基於Spring(SpringBoot、SpringMVC)框架寫的代碼時,發現很多類的代碼又回到瞭C++的形式。例如在使用一個LogService時,開發人員首先定義瞭一個interface,在LogService.java中:
public interface LogService { ServiceResult<Boolean> addLog(SysLogInfo logInfo); ServiceResult<Boolean> delLog(String id); }
然後定義瞭一個該接口的實現類,在LogServiceImpl.java中:
public class LogServiceImpl implements LogService{ @Override public ServiceResult<Boolean> addLog(SysLogInfo logInfo) { ...... } @Override public ServiceResult<Boolean> delLog(String id) { ...... } }
在需要實例化這個類的地方用瞭一個@Autowired註解註入。
public class LogController { @Autowired private LogService logservice; }
在問及開發人員為什麼要象這樣做時,其給瞭一個自信的回答:這是面向接口編程!
註意:這個設計中LogService.java類似於C++中的.h文件,LogServiceImpl.java類似於C++中的.cpp文件,這兩個文件共同定義瞭一個LogService類。當需要給這個類添加一個updateLog方法時,LogService.java和LogServiceImpl.java都需要被修改,又走回瞭C++的老路。這顯然是對面向接口編程的曲解。如果這樣都能算面向接口編程的話,那麼C++就成瞭一門天然的面向接口編程的語言,還何必去學那些復雜的設計模式。
不過這樣寫代碼有什麼問題嗎?其實也沒有太大問題,隻是代碼繁瑣一點而已(C++就是這樣的)。隻不過既然你選擇瞭Java語言,卻又寫成瞭C++的樣子,就好像在開一輛自動擋的汽車,卻一直撥到手動模式駕駛一樣。
四、正確理解面向接口編程
那麼什麼才是面向接口編程呢,其要點在於:接口是基於變化的抽象。在有可能變化的地方才需要接口。假設上面的例子中,寫日志的動作同時存在3種不同的實現:
1.寫到日志文件。
2.寫到數據庫。
3.寫到本地的一個日志服務的UDP端口。
那麼可以基於這個接口寫3個不同的實現類:
public class LogServiceFile implements LogService{ }
public class LogServiceDB implements LogService{ }
public class LogServiceUdp implements LogService{ }
當然此時如果還是使用下面的代碼會報錯,因為Autowired隻能裝配對應接口的唯一一個派生類的Bean,而此時存在3個派生類。
public class LogController { @Autowired private LogService logservice; }
需要改進成類似下面這個樣子,根據實際情況使用對應的派生類對象:
public class LogController { private LogService logservice; void writeLog(SysLogInfo logInfo){ logservice = GetLogServiceInst(); logservice.addLog(logInfo); } }
如果你的接口隻有一個實現類,而且在可以遇見的將來也不會有其他實現類,那麼還是建議你能簡化一點,采用最基本的類定義方式,減少代碼的復雜性。
到此這篇關於教你如何正確運用Java依賴註入的文章就介紹到這瞭,更多相關Java依賴註入內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- VC中控制臺程序創建窗口的實例方法
- 原來Java接口多實現還可以這樣玩
- JAVA Spring中讓人頭痛的JAVA大事務問題要如何解決你知道嗎
- 詳解Spring bean的註解註入之@Autowired的原理及使用
- SpringBoot AOP @Pointcut切入點表達式排除某些類方式