Java基础の教えはどのように正しく依存注入を使いますか?

4756 ワード

一、C++の非難
C++が一番非難されているところは、一つのクラスを定義するには二つのファイル、一つはhファイルと一つは.cppファイルを書く必要があります。例えば、CMainFrameクラスを定義し、manframe.hの内容は以下の通りである。

class CMainFrame : public CFrameWndEx
{
protected:
	CMainFrame();
public:
	virtual ~CMainFrame();
};
manframe.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);
};
manframe.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) {
		......
	}
}
udateLog方法を追加する必要がある場合は、LogService.javaを修正する必要があります。

public class LogService{
	public ServiceResult<Boolean> addLog (SysLogInfo logInfo) {
		......
	}
 
	public ServiceResult<Boolean> delLog(String id) {
		......
	}
 
	public ServiceResult<Boolean> updateLog (SysLogInfo logInfo) {
		......
	}
 
}
すべてが便利になりました。
三、誤用による退歩
しかし最近はSpring(Spring Boot、SprigMVC)フレームによるコードを見ていますが、多くの種類のコードが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類を定義しています。このクラスにアップロード方法を追加する必要がある時、LogService.javaとLogServiceImpl.javaは修正されて、またC++の古い道に戻ってきました。これは明らかに向こう側のインターフェースにプログラミングされた曲解である。もしこのようにすべてインターフェースに向かってプログラミングすることができるならば、C++は1つの天然のインターフェースに向かってプログラミングする言語になって、またあれらの複雑な設計のモードを学ぶ必要がありますか?
でも、このようにコードを書くと何か問題がありますか?実はあまり問題がないです。コードが煩雑なだけです。ただ、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依存注入の文章を正しく使う方法を教えます。これまでの記事を検索したり、次の関連記事を見たりしてください。今後もよろしくお願いします。