策略模式和依赖注入之间有什么区别?


Answers:


107

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

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

此外,您可以将策略作为方法的参数传递,而方法参数注入的相关概念并不广泛,并且仅在自动测试的上下文中使用。

策略专注于意图,并鼓励您创建一个遵循相同行为契约的具有不同实现的接口。DI不仅仅是关于某种行为的实现并提供它。

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


DI中只有一种实现方式使用的接口非常常见-那么在这种特殊情况下DI是什么?
Kalpesh Soni 2013年

3
这句话基本上说明了这一切:in a DI scenario it is more unusual that the dependencies of objects change during their lifetimes, while this is not uncommon with Strategy
谢尔盖·特尔谢夫斯基

策略:设计类,以便可以在运行时使用算法对其进行配置。DI:此类在运行时获得了注入的算法(策略对象)。来自w3sdesign.com上的GoF设计模式存储器。
GFranke '17

39

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

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

使用本示例,您可以看到不同之处:我们始终计划使用数据存储策略,而这正是我们传入的策略(真实的数据库实例)。但是,在开发和测试中,我们想使用不同的依赖关系,因此我们注入了不同的构想。


28

您可以将DI用作策略模式,这样就可以交换每个客户所需的算法,但是DI可以超越此范围,因为DI是将应用程序的各个部分分离的一种方式,而这并不属于应用程序的一部分。策略模式。

冒险地说,DI只是重命名的战略模式,因为它开始淡化了该战略模式的真正用途,即IMO。


2
我想我理解您的要旨,但是我不能正确地用语言表达出来……所以您所说的DI更像是一种实现模式,而策略更像是一种设计模式,而实现策略的一种方式是通过DI?
罗伯特·古尔德

1
听起来这是个好方法。DI不仅仅是一种策略模式。我对AOP感到困惑,人们认为这是一种工厂模式。我认为DI可以实现策略模式,因此您的措辞似乎很棒。:)
詹姆斯·布莱克

14

杜德(Dude),依赖注入是一种更通用的模式,它是关于抽象而不是具体的依赖,它是每个模式的一部分,但是策略模式是解决更具体问题的方法

这是维基百科的定义:

DI:

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

策略模式:

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

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


3
我特别喜欢您的解释中的“花花公子”部分。:-)
johey

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个检查。他们在注入测试类的测试双中使用了公共数据。由于数据隐藏原理,我无法使用生产代码执行此操作。我不想在生产代码中插入特殊目的的测试代码。公开数据必须位于不同的类中。

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


4

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

依赖注入不仅通过确定要使用的具体策略,还可以创建具体策略的实例并将其“注入”回调用模块,从而完善策略模式。即使只有一个依赖性,这也很有用,因为有关如何管理(初始化等)具体策略实例的知识也可以隐藏在策略对象内。


1

实际上,依赖项注入看起来也与Bridge模式非常相似。对我而言(根据定义),桥接模式用于容纳不同版本的实现,而策略模式用于完全不同的逻辑。但是示例代码看起来像是在使用DI。因此,也许DI只是一种技术或实现?


0

策略是使用依赖注入技能的场所。实现依赖项注入的实际方法如下:

  1. 大事记
  2. 单位/结构图(或以编程方式)等的配置文件
  3. 扩展方法
  4. 抽象工厂模式
  5. 控制模式的反转(由策略和抽象工厂使用)

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

实际上,策略并没有谈论依赖注入。如果需要,可以通过Strategy模式中的Abstract Factory来完成。策略只讨论创建具有接口的类家族并对其进行“播放”。在玩游戏时,如果我们发现班级在不同的层中,那么我们必须自己注入,而不是策略的工作。


0

如果我们考虑SOLID原则-我们将策略模式用于开放式封闭式原则,将依赖项注入用于依赖项倒置原则


1
我不确定我是否可以遵循,您能否详细说明一下策略与开放/封闭原则之间的关系以及DI与DIP之间的关系?
亚当·帕金
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.