多くのJava DIフレームワーク(Spring、CDIなど)は、ユーザーが設計した修飾子アノテーションを介して、ある種の修飾インジェクションを実装します。
interface Engine { .. }
@Powerful
public PowerfulEngine implements Engine { .. }
public class Car {
@Inject @Powerful
private Engine engine; // will inject a PowerfulEngine instance
}
そしてこれは、オブジェクト間の疎結合を有効にする方法として宣伝されています。しかし、たった1つの@Powerful
プロジェクトでの実装が許可されますが、これがこれよりも結合されている方法がわかりません。
public class Car {
@Inject
private PowerfulEngine engine;
}
単体テストを作成するときに、オブジェクトではなくインターフェイスをモックする方が漠然とより良いことがわかります。実際には、必要な実装の正確な種類を指定する必要があります。注入。
私は何かが足りないのですか?
最初の例では、Engine
クラス内でCar
属性を使用しています。 Engine
はインターフェースを介して公開されるため、インターフェース宣言に依存します。 Engine
という名前のPowerful
インターフェースの実装を要求しているアノテーションを使用して、この特定の場合に使用するインターフェースの正確な実装をコンパイラーに知らせますが、Javaコンパイラでは、インターフェイスの宣言以外のメソッドを使用することはできません。
最初の例はどのように結合されていませんか?
PowerfulEngine
にEngine
インターフェースに含まれるメソッドのみが含まれるシナリオを検討する場合、つまり、クラス自体が独自のメソッドを追加しない場合、結合はどちらの場合もまったく同じです。 PowerfulEngine
クラスに特定のメソッドを追加し始めますそしてそれらを使用して状況は大きく異なります。
説明のために、例を少し拡張してみましょう。
interface Engine
{
void run();
}
class PowerfulEngine implements Engine
{
@Override
public void run() {
System.out.println("Running a powerful engine.");
}
}
class RegularEngine implements Engine
{
@Override
public void run() {
System.out.println("Running a regular engine.");
}
}
class Car
{
private Engine engine;
public void go() {
engine.run();
}
}
Car
クラスには、go
のrun
メソッドを呼び出すEngine
メソッドがあります。これで、Car
オブジェクトに挿入されたクラスに応じて、システムは強力なエンジンの実行を出力します。または通常のエンジンを実行しています。
Car
クラスは、run
のEngine
メソッドを使用できることを認識しています。これは、それを提供し、実際の実装を気にしないためです。すでに述べたように、Engine
依存関係をPowerfulEngine
依存関係に置き換えることにした場合、この場合はEngine
で宣言されたメソッドのみを使用しているため、何も起こりません。インターフェース。
しかし、PowerfulEngine
クラスに別のメソッドを追加してそれを使用する場合、それはまったく異なるケースです。
PowerfulEngine
クラスとCar
クラスを少し変更してみましょう。
class PowerfulEngine implements Engine
{
@Override
public void run() {
System.out.println("Running a powerful engine.");
}
public void rev() {
System.out.println("Revving a powerful engine.");
}
}
class Car
{
private PowerfulEngine engine;
public void go() {
engine.rev();
engine.run();
}
}
RegularEngine
の代わりにPowerfulEngine
を使用することにした場合、どうなるでしょうか。 RegularEngine
クラスにはrev
メソッドがなく、Engine
インターフェイスもないため、アプリケーションはコンパイル中に失敗します。存在しないメソッドにアクセスすることになります。
全体として、具象クラスに依存していて、クラスが実装する特定のインターフェースで使用可能なメソッドのみを使用している場合は、クラスに直接依存するのではなく、インターフェースに依存する方がよいため、他の人に明示的に通知します:ねえ、私は実際には実装に依存していません、私はそれを気にしません、私はインターフェースがメソッドを介してこれらのコントラクトを持っていることだけを気にし、それらを使用できます。
抽象化によって公開されていないクラスの特定のメソッドを使用する必要がある場合は、具体的な実装に依存する以外に選択肢はありません。