107

策略模式和依赖注入都允许我们在运行时设置/注入对象。策略模式和依赖注入有什么区别?

4

9 回答 9

121

DI 和 Strategy 以相同的方式工作,但 Strategy 用于更细粒度和短暂的依赖关系。

当对象配置有“固定”策略时,例如在构造对象时,策略和 DI 之间的区别会变得模糊。但是在 DI 场景中,对象的依赖关系在其生命周期中发生变化是更不寻常的,而这在 Strategy 中并不少见。

此外,您可以将策略作为参数传递给方法,而方法参数注入的相关概念并不普遍,并且主要仅用于自动化测试的上下文中。

策略侧重于意图,并鼓励您创建一个具有不同实现的接口,这些实现遵循相同的行为契约。DI 更多的是关于实现某些行为并提供它。

使用 DI,您可以出于其他原因分解您的程序,而不仅仅是为了能够交换部分实现。只有一种实现的 DI 中使用的接口非常常见。只有一个具体实现(曾经)的“策略”不是真正的问题,但可能更接近 DI。

于 2010-11-14T08:53:55.580 回答
43

不同之处在于他们正在努力实现的目标。策略模式用于您知道要换出实现的情况。例如,您可能希望以不同的方式格式化数据 - 您可以使用策略模式换出 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());

依赖注入有一些理由,但主要是为了测试。您可能会遇到某种情况,即您拥有某种持久性引擎(例如数据库)。但是,当您重复运行测试时,使用真实数据库可能会很痛苦。因此,对于您的测试用例,您将注入一个虚拟数据库,这样您就不会产生这种开销。

使用此示例,您可以看到不同之处:我们总是计划使用数据存储策略,并且是我们传入的策略(真正的数据库实例)。但是在开发和测试中,我们要使用不同的依赖,所以我们注入不同的concretions。

于 2010-11-14T09:55:51.193 回答
30

您可以将 DI 用作一种策略模式,因此您可以交换每个客户所需的算法,但 DI 可以超越这一点,因为它只是一种将应用程序的各个部分解耦的方法,而这不是应用程序的一部分策略模式。

说 DI 只是一个重命名的战略模式是有风险的,因为这开始淡化战略模式的真正用途,IMO。

于 2009-05-05T04:59:12.667 回答
16

伙计,依赖注入是一种更通用的模式,它是关于对抽象而不是具体的依赖,它是每个模式的一部分,但是策略模式是针对更具体问题的解决方案

这是来自维基百科的定义:

迪:

面向对象计算机编程中的依赖注入 (DI) 是一种设计模式,其核心原则是将行为与依赖解析分离。换句话说:一种用于解耦高度依赖的软件组件的技术。

策略模式:

在计算机编程中,策略模式(也称为策略模式)是一种特定的软件设计模式,可以在运行时选择算法。

策略模式旨在提供一种方法来定义一系列算法,将每个算法封装为一个对象,并使它们可互换。策略模式让算法独立于使用它们的客户端而变化。

于 2010-11-14T07:02:07.590 回答
7

策略是用于改变事物计算方式的更高级别的事物。使用依赖注入,您不仅可以更改计算的方式,还可以更改存在的内容。

对我来说,使用单元测试时会很清楚。对于生产代码执行,您隐藏了所有数据(即私有或受保护);而对于单元测试,大部分数据都是公开的,因此我可以使用 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 次检查。他们使用了测试替身中的公共数据,这些数据被注入到被测类中。由于数据隐藏原则,我无法使用生产代码执行此操作。我不想在生产代码中插入特殊用途的测试代码。公共数据必须属于不同的类别。

测试替身被注入。这与策略不同,因为它影响数据而不仅仅是功能。

于 2009-05-10T03:53:14.630 回答
4

依赖注入是我将简要解释的策略模式的改进。通常需要在运行时在几个替代模块之间进行选择。这些模块都实现了一个通用接口,因此它们可以互换使用。策略模式的目的是通过将决策过程封装到一个单独的对象(我将其称为策略对象)中来消除决定使用哪个模块(即哪个“具体策略”或依赖项)的负担。

依赖注入改进了策略模式,不仅决定使用哪个具体策略,而且创建具体策略的实例并将其“注入”回调用模块。即使只有一个依赖项,这也很有用,因为如何管理(初始化等)具体策略实例的知识也可以隐藏在策略对象中。

于 2011-08-01T02:57:29.483 回答
1

实际上,依赖注入看起来也非常类似于桥接模式。对我来说(根据定义),桥模式是为了适应不同版本的实现,而策略模式是为了完全不同的逻辑。但是示例代码看起来像是在使用 DI。所以也许 DI 只是一种技术或实现?

于 2011-09-13T23:10:43.530 回答
1

策略是使用依赖注入技能的舞台。实现依赖注入的真正方法如下:-

  1. 活动
  2. 统一/结构图(或以编程方式)等的配置文件。
  3. 扩展方法
  4. 抽象工厂模式
  5. 控制模式的反转(策略和抽象工厂都使用)

有一件事使战略与众不同。正如您在 Unity 中所知道的,当应用程序启动时,所有依赖项都已设置,我们无法进一步更改它。但策略支持运行时依赖项更改。但是我们必须管理/注入依赖,而不是策略的责任!

实际上策略并没有谈论依赖注入。如果需要,可以通过策略模式中的抽象工厂来完成。Strategy 只谈论创建一系列具有接口的类并“玩”它。在玩的时候,如果我们发现类在不同的层,那么我们必须自己注入它,而不是策略的工作。

于 2012-10-23T14:30:48.260 回答
1

如果我们考虑 SOLID 原则 - 我们使用开放封闭原则的策略模式和依赖倒置原则的依赖注入

于 2018-05-17T02:24:01.300 回答