Spring がコードでインターフェースを使用することをどのように奨励しているかについて、私はいたるところで読みました。見えません。spring xml 構成には、インターフェースの概念はありません。Spring のどの部分が実際にインターフェイスの使用を推奨していますか (ドキュメント以外)?
9 に答える
クラスのインターフェースを定義すると、依存性注入に役立ちます。Spring 構成ファイル自体には、インターフェースに関するものは何もありません。クラスの名前を入力するだけです。
しかし、「同等の」機能を提供する別のクラスを注入したい場合は、インターフェイスを使用すると非常に役立ちます。
たとえば、Web サイトのコンテンツを分析するクラスがあり、Spring を注入しているとします。注入するクラスが実際のクラスが何であるかを知っている場合、それを変更するには、多くのコードを変更して別の具象クラスを使用する必要があります。しかし、インターフェースを作成した場合は、モックアップや、本質的に同じことを行う別のもの (aなど) と同じAnalyzer
くらい簡単に、元のインターフェースを挿入することができます。これらのいずれかを使用するには、コードを変更してクラスを変更するのではなく、Spring 構成ファイルに挿入するクラス名を変更するだけです。DefaultAnalyzer
DummyAnalyzer
PageByPageAnalyzer
有用性を実感し始めるまでに、約 1 プロジェクト半かかりました。(エンタープライズ言語で) 有用になるほとんどのものと同様に、プロジェクトが成長し始めるまでは、最初は無意味な作業の追加のように思えます。
依存性逆転の原則は、これをよく説明しています。特に、図 4。
A. 高レベル モジュールは低レベル モジュールに依存すべきではありません。どちらも抽象化に依存する必要があります。
B. 抽象化は詳細に依存すべきではありません。詳細は抽象化に依存する必要があります。
上記のリンクの例を Java に翻訳する:
public class Copy {
private Keyboard keyboard = new Keyboard(); // concrete dependency
private Printer printer = new Printer(); // concrete dependency
public void copy() {
for (int c = keyboard.read(); c != KeyBoard.EOF) {
printer.print(c);
}
}
}
依存関係の反転を使用すると、次のようになります。
public class Copy {
private Reader reader; // any dependency satisfying the reader interface will work
private Writer writer; // any dependency satisfying the writer interface will work
public void copy() {
for (int c = reader.read(); c != Reader.EOF) {
writer.write(c);
}
}
public Copy(Reader reader, Writer writer) {
this.reader = reader;
this.writer = writer;
}
}
Copy
キーボードからプリンターへのコピー以上の機能をサポートするようになりました。
コードを変更することなく、任意のものからReader
任意のものにコピーできます。Writer
そして今、Spring で:
<bean id="copy" class="Copy">
<constructor-arg ref="reader" />
<constructor-arg ref="writer" />
</bean>
<bean id="reader" class="KeyboardReader" />
<bean id="writer" class="PrinterWriter" />
多分:
<bean id="reader" class="RemoteDeviceReader" />
<bean id="writer" class="DatabaseWriter" />
ここでの答えのほとんどは、「実装を簡単に交換できる」という何らかの形ですが、答えられないと思うのはその理由ですか? 部。それに対する答えは、ほぼ確実にテスト可能であると思います。Spring やその他の IOC フレームワークを使用するかどうかに関係なく、依存性注入を使用すると、コードのテストが容易になります。たとえば、PrinterWriter ではなくライターの場合は、単体テストで Writer インターフェイスをモックし、コードが期待どおりに呼び出していることを確認できます。クラスの実装に直接依存している場合、唯一のオプションはプリンターまで歩いて確認することですが、これはあまり自動化されていません。さらに、クラスへの呼び出しの結果に依存する場合、それをモックできないと、テスト内のすべてのコード パスに到達できなくなる可能性があります。したがって、品質が低下する可能性があります (潜在的に) 簡単に言えば、オブジェクト グラフの作成をアプリケーション ロジックから分離する必要があります。そうすることで、コードのテストが容易になります。
おそらく、Spring がインターフェイスの使用をどのように奨励しているかは、ドキュメントから明らかではないかもしれません。
以下にいくつかの例を示します。
いくつかの方法 (クラスパス、絶対ファイルパス、URL など) で参照されるリソース (ファイルなど) から読み取る必要があるクラスを作成しているとします。
org.springframework.core.io.Resource
クラスで(インターフェイス)プロパティを定義する必要があります。org.springframework.core.io.ClassPathResource
次に、Spring 構成ファイルで、実際の実装クラス ( 、など)org.springframework.core.io.FileSystemResource
を選択するだけです。org.springframework.core.io.UrlResource
Spring は基本的に非常に一般的なファクトリとして機能します。Spring の AOP 統合を利用したい場合 (たとえば、トランザクション インターセプターを追加するため)、ほとんどの場合、インターフェースを定義する必要があります。Spring 構成ファイルでインターセプト ポイントを定義すると、Spring がインターフェースに基づいてプロキシを生成します。
これらは私が個人的に経験した例です。私はそこにもっとたくさんあると確信しています。
インターフェイスからプロキシを生成するのは簡単です。
Spring アプリを見ると、サービスと永続化のインターフェイスが表示されます。春のイディオムは確かにインターフェイスの使用を奨励しています。それ以上明確になることはありません。
インターフェースを使用しないと、自動配線の失敗のリスクがあります。Spring がBean の Proxy クラスを作成することがあります。この Proxy クラスはサービス実装の子クラスではありませんが、そのすべてのインターフェイスを再実装します。Spring はこの Bean のインスタンスをオートワイヤーしようとしますが、この Proxy クラスは Bean クラスと互換性がありません。そのため、Bean クラスでフィールドを宣言すると、「安全でないフィールド割り当て」例外が発生する可能性があります。
Spring がいつサービスをプロキシするかを合理的に知ることはできません (また、そうするべきではありません)。そのため、これらの驚きから身を守るために、インターフェイスを宣言し、自動配線されたフィールドを宣言するときにこのインターフェイスを使用することをお勧めします。
Spring は、どこでもインターフェースを使用することを強制しません。それは良い習慣です。具象クラスではなくインターフェースであるいくつかのプロパティを持つ Bean がある場合、同じインターフェースを実装するモックアップを使用していくつかのオブジェクトを簡単に切り替えることができます。これは、特定のテスト ケースに役立ちます。
たとえば、Hibernate サポート クラスを使用する場合は、DAO のインターフェイスを定義してから、それを個別に実装できます。インターフェースを持つ利点は、Spring インターセプターを使用してインターフェースを構成できることです。これにより、コードを簡素化できます。HibernateExceptions をキャッシングし、finally セグメントでセッションを閉じるコードを記述する必要はありません。また、プログラムでトランザクションを定義する必要もありません。Spring で宣言的にすべてのものを構成するだけです。
クイック アンド ダーティー アプリを作成する場合、JDBC を使用して単純な DAO を実装するか、最終バージョンでは使用しない単純なフレームワークを実装できます。これらのコンポーネントが共通のインターフェースを実装していれば、それらのコンポーネントを簡単に切り替えることができます。