web-dev-qa-db-ja.com

適格な依存性注入と結合

多くの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;  
}

単体テストを作成するときに、オブジェクトではなくインターフェイスをモックする方が漠然とより良いことがわかります。実際には、必要な実装の正確な種類を指定する必要があります。注入。

私は何かが足りないのですか?

1
fabienbk

最初の例では、Engineクラス内でCar属性を使用しています。 Engineはインターフェースを介して公開されるため、インターフェース宣言に依存します。 Engineという名前のPowerfulインターフェースの実装を要求しているアノテーションを使用して、この特定の場合に使用するインターフェースの正確な実装をコンパイラーに知らせますが、Javaコンパイラでは、インターフェイスの宣言以外のメソッドを使用することはできません。

最初の例はどのように結合されていませんか?

PowerfulEngineEngineインターフェースに含まれるメソッドのみが含まれるシナリオを検討する場合、つまり、クラス自体が独自のメソッドを追加しない場合、結合はどちらの場合もまったく同じです。 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クラスには、gorunメソッドを呼び出すEngineメソッドがあります。これで、Carオブジェクトに挿入されたクラスに応じて、システムは強力なエンジンの実行を出力します。または通常のエンジンを実行しています。

Carクラスは、runEngineメソッドを使用できることを認識しています。これは、それを提供し、実際の実装を気にしないためです。すでに述べたように、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インターフェイスもないため、アプリケーションはコンパイル中に失敗します。存在しないメソッドにアクセスすることになります。


全体として、具象クラスに依存していて、クラスが実装する特定のインターフェースで使用可能なメソッドのみを使用している場合は、クラスに直接依存するのではなく、インターフェースに依存する方がよいため、他の人に明示的に通知します:ねえ、私は実際には実装に依存していません、私はそれを気にしません、私はインターフェースがメソッドを介してこれらのコントラクトを持っていることだけを気にし、それらを使用できます。

抽象化によって公開されていないクラスの特定のメソッドを使用する必要がある場合は、具体的な実装に依存する以外に選択肢はありません。

2
Andy