web-dev-qa-db-ja.com

戦略パターンと依存性注入の違いは何ですか?

戦略パターンと依存性注入の両方により、実行時にオブジェクトを設定/注入できます。戦略パターンと依存性注入の違いは何ですか?

90
Nero

DIとStrategyは同じように機能しますが、Strategyはよりきめ細かく短期間の依存関係に使用されます。

オブジェクトが「固定」戦略で構成されている場合、たとえばオブジェクトが構築されている場合、戦略とDIの区別がぼやけます。しかし、DIシナリオでは、ライフタイム中にオブジェクトの依存関係が変化することはよりまれですが、これはStrategyでは珍しいことではありません。

また、メソッドへの引数としてメソッドを渡すことができますが、メソッド引数インジェクションの関連概念は広く普及しておらず、ほとんどが自動テストのコンテキストでのみ使用されます。

ストラテジーは意図に焦点を合わせ、同じ動作規約に従う異なる実装でインターフェースを作成することをお勧めします。 DIは、ある動作を実装して提供するだけのものです。

DIを使用すると、実装の一部を交換できるだけでなく、他の理由でプログラムを分解できます。実装が1つしかないDIで使用されるインターフェイスは非常に一般的です。具体的な実装が1つだけの「戦略」は、実際の問題ではありませんが、おそらくDIに近いでしょう。

97
eljenso

違いは、彼らが達成しようとしていることです。 Strategyパターンは、実装を交換したいことがわかっている状況で使用されます。例として、さまざまな方法でデータをフォーマットすることができます-ストラテジーパターンを使用して、XMLフォーマッターまたはCSVフォーマッターなどを交換できます。

依存性注入は、ユーザーがランタイム動作を変更しようとしていないという点で異なります。上記の例に従って、XMLフォーマッタを使用するXMLエクスポートプログラムを作成している可能性があります。このようにコードを構築するのではなく:

public class DataExporter() {
  XMLFormatter formatter = new XMLFormatter();
}

コンストラクターでフォーマッターを「注入」します。

public class DataExporter {
  IFormatter formatter = null;

  public DataExporter(IDataFormatter dataFormatter) {
    this.formatter = dataFormatter;
  }
}

DataExporter exporter = new DataExporter(new XMLFormatter());

依存性注入にはいくつかの正当化がありますが、主なものはテスト用です。ある種の永続化エンジン(データベースなど)がある場合があります。ただし、テストを繰り返し実行しているときに実際のデータベースを使用するのは面倒な場合があります。そのため、テストケースでは、ダミーデータベースを挿入して、そのオーバーヘッドが発生しないようにします。

この例を使用すると、違いがわかります。常にデータストレージ戦略を使用することを計画しており、それが渡される戦略(実際のDBインスタンス)です。ただし、開発とテストでは、異なる依存関係を使用するため、異なるコンクリーションを注入します。

37
tsimon

DIを戦略パターンとして使用できるため、各顧客に必要なアルゴリズムを交換できますが、DIはアプリケーションの一部を分離する方法であるため、それを超えることができます。戦略パターン。

DIは、戦略パターンの実際の目的であるIMOを希釈し始めるため、名前が変更された戦略パターンであると言うのは危険です。

27
James Black

依存性注入はより一般的なパターンであり、コンクリートではなく抽象化への依存性に関するものであり、すべてのパターンの一部ですが、戦略パターンはより具体的な問題の解決策です

これはウィキペディアの定義です:

DI:

オブジェクト指向のコンピュータープログラミングにおける依存性注入(DI)は、動作を依存関係の解決から分離するという中核的な原則を備えた設計パターンです。言い換えると、高度に依存するソフトウェアコンポーネントを分離する技術です。

戦略パターン:

コンピュータープログラミングでは、戦略パターン(ポリシーパターンとも呼ばれます)は特定のソフトウェア設計パターンであり、実行時にアルゴリズムを選択できます。

戦略パターンは、アルゴリズムのファミリーを定義し、各アルゴリズムをオブジェクトとしてカプセル化し、それらを交換可能にする手段を提供することを目的としています。戦略パターンにより、アルゴリズムはそれを使用するクライアントとは独立して変化します。

13
Jahan Zinedine

戦略は、物事の計算方法を変更するために使用される上位レベルのものです。依存性注入を使用すると、物事の計算方法だけでなく、そこにあるものも変更できます。

私にとっては、単体テストを使用すると明らかになります。本番コードの実行では、すべてのデータが非表示になっています(つまり、プライベートまたは保護されています)。一方、単体テストでは、ほとんどのデータが公開されているため、Assertsで確認できます。


戦略の例:

public class Cosine {
  private CalcStrategy strat;

  // Constructor - strategy passed in as a type of DI
  public Cosine(CalcStrategy s) {
    strat = s;
  }
}

public abstract class CalcStrategy {
  public double goFigure(double angle);
}

public class RadianStrategy extends CalcStrategy {
  public double goFigure(double angle) {
    return (...);
  }
}
public class DegreeStrategy extends CalcStrategy {
  public double goFigure(double angle) {
    return (...);
  }
}

戦略間で異なる公開データはないことに注意してください。別の方法もありません。両方の戦略は、すべて同じ機能とシグネチャを共有しています。


依存性注入の場合:

public class Cosine {
  private Calc strat;

  // Constructor - Dependency Injection.
  public Cosine(Calc s) {
    strat = s;
  }
}

public class Calc {
  private int numPasses = 0;
  private double total = 0;
  private double intermediate = 0;

  public double goFigure(double angle) {
    return(...);
}

public class CalcTestDouble extends Calc {
  // NOTICE THE PUBLIC DATA.
  public int numPasses = 0;
  public double total = 0;
  public double intermediate = 0;
  public double goFigure(double angle) {
    return (...);
  }
}

使用する:

public CosineTest {

  @Test
  public void testGoFigure() {
    // Setup
    CalcTestDouble calc = new CalcTestDouble();
    Cosine instance = new Cosine(calc);

    // Exercise
    double actualAnswer = instance.goFigure(0.0);

    // Verify
    double tolerance = ...;
    double expectedAnswer = ...;
    assertEquals("GoFigure didn't work!", expectedAnswer,
         actualAnswer, tolerance);

    int expectedNumPasses = ...;
    assertEquals("GoFigure had wrong number passes!",
        expectedNumPasses, calc.numPasses);

    double expectedIntermediate = ...;
    assertEquals("GoFigure had wrong intermediate values!",
        expectedIntermediate, calc.intermediate, tolerance);
  }
}

最後の2つのチェックに注意してください。彼らは、テスト対象のクラスに注入されたテストダブルのパブリックデータを使用しました。データ隠蔽の原理のため、私は生産コードでこれを行うことができませんでした。実稼働コードに特別な目的のテストコードを挿入したくありませんでした。公開データは別のクラスにある必要がありました。

テストダブルが注入されました。関数だけでなくdataに影響するため、これは単なる戦略とは異なります。

6
Edward Ames

依存性注入は、簡単に説明する戦略パターンの改良版です。多くの場合、実行時に複数の代替モジュールから選択する必要があります。これらのモジュールはすべて共通のインターフェースを実装しているため、交換可能に使用できます。戦略パターンの目的は、意思決定プロセスを戦略オブジェクトと呼ぶ別のオブジェクトにカプセル化することにより、どのモジュールを使用するか(つまり、「具体的な戦略」または依存関係)を決定する負担を取り除くことです。

依存性注入は、使用する具体的な戦略を決定するだけでなく、具体的な戦略のインスタンスを作成し、呼び出しモジュールに「注入」することで、戦略パターンを改良します。これは、具体的な戦略インスタンスを管理(初期化など)する方法に関する知識も戦略オブジェクト内に隠すことができるため、単一の依存関係しかない場合でも役立ちます。

4

実際、依存性注入もBridgeパターンと非常によく似ています。私にとって(そして定義によれば)、Bridgeパターンは実装の異なるversionsに対応することですが、Strategyパターンはまったく異なるロジック用です。しかし、サンプルコードはDIを使用しているように見えます。では、DIは単なる技術または実装なのでしょうか?

1
Calvin

戦略は、依存性注入スキルを使用するための領域です。依存性注入を実装する実際の方法は次のとおりです。

  1. イベント
  2. ユニティ/構造マップ(またはプログラム)などの構成ファイル.
  3. 拡張メソッド
  4. 抽象ファクトリーパターン
  5. 制御パターンの反転(戦略と抽象ファクトリーの両方で使用)

戦略を際立たせるものが1つあります。 Unityで知っているように、アプリケーションの起動時にすべての依存関係が設定されており、これ以上変更することはできません。ただし、戦略はランタイム依存関係の変更をサポートします。しかしWEは、戦略の責任ではなく、依存関係を管理/注入する必要があります!

実際、戦略は依存性注入については言及していません。必要に応じて、ストラテジーパターン内の抽象ファクトリーで実行できます。ストラテジーは、インターフェースを備えたクラスのファミリーを作成し、それを「プレイ」することについてのみ話します。プレイ中に、クラスが異なるティアにある場合、ストラテジーの仕事ではなく、自分でクラスを注入する必要があります。

0
Blue Clouds

SOLID原則-オープンクローズドプリンシパルには戦略パターンを、依存性反転原理には依存性注入を使用する場合

0
Sumeet Patil