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

Ner*_*ero 91 design-patterns dependency-injection strategy-pattern

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

elj*_*nso 97

DI和策略以相同的方式工作,但策略用于更细粒度和短期的依赖关系.

当对象配置了"固定"策略时,例如在构造对象时,策略和DI之间的区别模糊.但是在DI场景中,对象的依赖关系在其生命周期中会发生变化,这种情况更为罕见,而战略并不少见.

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

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

使用DI,您可以出于其他原因分解您的程序,而不仅仅是为了能够交换部分实现.DI中使用的接口只有一个实现是很常见的.只有一个具体实施(永远)的"战略"不是一个真正的问题,但可能更接近DI.

  • 这句话基本上解释了这一切:`在 DI 场景中,对象的依赖关系在它们的生命周期中发生变化是更不寻常的,而这在 Strategy 中并不少见` (3认同)

tsi*_*mon 37

不同之处在于他们正在努力实现的目标.策略模式用于您知道要替换实现的情况.例如,您可能希望以不同方式格式化数据 - 您可以使用策略模式来交换XML格式化程序或CSV格式化程序等.

依赖注入的不同之处在于用户不会尝试更改运行时行为.按照上面的示例,我们可能正在创建一个使用XML格式化程序的XML导出程序.而不是像这样构造代码:

public class DataExporter() {
  XMLFormatter formatter = new XMLFormatter();
}
Run Code Online (Sandbox Code Playgroud)

你会在构造函数中"注入"格式化程序:

public class DataExporter {
  IFormatter formatter = null;

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

DataExporter exporter = new DataExporter(new XMLFormatter());
Run Code Online (Sandbox Code Playgroud)

Dependency Injection有一些理由,但主要用于测试.您可能遇到某种类型的持久性引擎(例如数据库).但是,当您重复运行测试时,使用真实数据库可能会很麻烦.因此,对于您的测试用例,您将注入一个虚拟数据库,这样您就不会产生这种开销.

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


Jam*_*ack 27

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

如果说DI只是一种重新命名的战略模式,那将开始削弱战略模式的真正含义,IMO将是危险的.

  • 我想我理解你的要点,但我不能正确地说出来......所以你说DI更多的是一种实现模式,而策略更像是一种设计模式,实现策略的一种方法是通过DI? (2认同)

Jah*_*ine 13

Dude,依赖注入是一种更普遍的模式,它是关于抽象的依赖而不是具体结构,它是每个模式的一部分,但战略模式是更具体问题的解决方案

这是维基百科的定义:

DI:

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

策略模式:

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

策略模式旨在提供定义算法族的方法,将每个算法封装为对象,并使它们可互换.策略模式允许算法独立于使用它们的客户端.

  • 我特别喜欢你解释中的“伙计”部分。:-) (4认同)

小智 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 (...);
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,策略之间没有不同的公共数据.也没有任何不同的方法.两种策略共享所有相同的功能和签名.


现在为依赖注入:

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 (...);
  }
}
Run Code Online (Sandbox Code Playgroud)

使用:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

注意最后2个检查.他们使用注入被测试类的测试双重中的公共数据.由于数据隐藏原则,我无法使用生产代码执行此操作.我不想在生产代码中插入专用测试代码.公共数据必须属于不同的类别.

注射试验双.这不仅仅是一种策略,因为它影响数据而不仅仅是功能.