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

发布于 2024-07-19 13:34:16 字数 50 浏览 5 评论 0原文

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

Strategy pattern and Dependency Injection both allow us to set / inject objects at run time. What is the difference between Strategy pattern and Dependency Injection?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

云之铃。 2024-07-26 13:34:16

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

当对象配置了“固定”策略时,例如构造对象时,策略和 DI 之间的区别就模糊了。 但在 DI 场景中,对象的依赖关系在其生命周期内发生变化的情况更为罕见,而这对于策略来说并不罕见。

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

策略侧重于意图,并鼓励您使用遵守相同行为契约的不同实现来创建接口。 DI 更多的是实现某些行为并提供它。

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

DI and Strategy work in the same way, but Strategy is used for more fine-grained and short-lived dependencies.

When an object is configured with a "fixed" Strategy, for example when the object is constructed, the distinction between Strategy and DI blurs. But in a DI scenario it is more unusual that the dependencies of objects change during their lifetimes, while this is not uncommon with Strategy.

Also, you can pass strategies as arguments to methods, while the related concept of method argument injection is not widespread and mostly used in the context of automated testing only.

Strategy focuses on intent and encourages you to create an interface with different implementations that obey the same behavioral contract. DI is more about just having an implementation of some behavior and providing it.

With DI you can decompose your program for other reasons than just to be able to swap parts of the implementation. An interface used in DI with only one implementation is very common. A "Strategy" with only one concrete implementation (ever) is not a real problem but is probably closer to DI.

萌无敌 2024-07-26 13:34:16

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

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

使用此示例,您可以看到差异:我们始终计划使用数据存储策略,并且它是我们传入的策略(真正的数据库实例)。 然而,在开发和测试中,我们想要使用不同的依赖项,因此我们注入不同的具体内容。

The difference is what they are trying to achieve. The Strategy pattern is used in situations where you know that you want to swap out implementations. As an example, you might want to format data in different ways - you could use the strategy pattern to swap out an XML formatter or CSV formatter, etc.

Dependency Injection is different in that the user is not trying to change the runtime behaviour. Following the example above, we might be creating an XML export program that uses an XML formatter. Rather than structuring the code like this:

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

you would 'inject' the formatter in the constructor:

public class DataExporter {
  IFormatter formatter = null;

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

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

There are a few justifications for Dependency Injection, but the primary one is for testing. You might have a case where you have a persistence engine of some sort (such as a database). However, it can be a pain to use a real database when you're running tests repeatedly. So, for your test cases, you would inject a dummy database, so that you don't incur that overhead.

Using this example, you can see the difference: we always plan on using a data storage strategy, and it's the one that we pass in (the real DB instance). However, in development and testing, we want to use different dependencies, so we inject different concretions.

时光病人 2024-07-26 13:34:16

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

在我看来,说 DI 只是重命名的策略模式是有风险的,因为这开始淡化策略模式的真正用途。

You can use DI as a strategy pattern, so you can swap in the algorithm that is needed for each customer, but DI can go beyond that as it is a way to just decouple the parts of an application, which wouldn't be part of the strategy pattern.

It would be risky to say that DI is just a renamed strategy pattern as that starts to dilute what the strategy pattern really is for, IMO.

揪着可爱 2024-07-26 13:34:16

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

这是维基百科的定义:

DI:

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

策略模式:

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

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

Dude, dependency injection is a more general pattern, and it's about dependency on abstractions not concretions and it's a part of every pattern, but Strategy pattern is a solution to more specific problem

this is the definition from wikipedia:

DI:

Dependency injection (DI) in
object-oriented computer programming
is a design pattern with a core
principle of separating behavior from
dependency resolution. In other words:
a technique for decoupling highly
dependent software components.

Strategy Pattern:

In computer programming, the strategy
pattern (also known as the policy
pattern) is a particular software
design pattern, whereby algorithms can
be selected at runtime.

The strategy pattern is intended to
provide a means to define a family of
algorithms, encapsulate each one as an
object, and make them interchangeable.
The strategy pattern lets the
algorithms vary independently from
clients that use them.

恏ㄋ傷疤忘ㄋ疼 2024-07-26 13:34:16

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

对我来说,使用单元测试时就变得很清楚了。 对于生产代码执行,您可以隐藏所有数据(即私有或受保护); 而在单元测试中,大部分数据都是公开的,因此我可以使用断言来查看它。


策略示例:

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 个检查。 他们使用了测试替身中的公共数据,并将其注入到被测试的类中。 由于数据原因,我无法使用生产代码执行此操作
隐藏原则。 我不想在生产代码中插入特殊用途的测试代码。 公共数据必须属于不同的类别。

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

Strategies are higher-level things that are used to change how things are computed. With dependency injection, you can change not just how things are computed but also change what is there.

For me, it becomes clear when using unit tests. For production code execution, you have all the data hidden (i.e. private or protected); whereas, with unit tests, most of the data is public so I can look at it with the Asserts.


Example of strategy:

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 (...);
  }
}

Notice that there is no public data that is different between the strategies. Nor is there any different methods. Both strategies share all the same functions and signatures.


Now for the dependency injection:

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 (...);
  }
}

Use:

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);
  }
}

Notice the last 2 checks. They used the public data in the test double that was injected into the class under test. I couldn't do this with production code because of the data
hiding principle. I didn't want to have special purpose testing code inserted in the production code. The public data had to be in a different class.

The test double was injected. That is different than just a strategy since it affected data and not just functions.

悲凉≈ 2024-07-26 13:34:16

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

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

Dependency injection is a refinement of the strategy pattern which I will briefly explain. It is often necessary to choose between several alternative modules at runtime. These modules all implement a common interface so that they can be used interchangeably. The purpose of the strategy pattern is to remove the burden of deciding upon which of the modules to use (ie which "concrete strategy" or dependency) by encapsulating the decision-making process into a separate object which I will call the strategy object.

Dependency injection refines the strategy pattern by not only deciding which concrete strategy to use but creating an instance of the concrete strategy and "injecting" it back into the calling module. This is useful even if there is only a single dependency as the knowledge of how to manage (initialise etc) the concrete strategy instance can also be hidden within the strategy object.

梦中楼上月下 2024-07-26 13:34:16

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

Actually, dependency injection also looks very similar to the Bridge pattern. To me (and according to the definition), the Bridge pattern is to accommodate different versions of the implementation, while the Strategy pattern is for the totally different logic. But the sample code looks like it's using DI. So maybe DI is just a technic or implementation?

夜清冷一曲。 2024-07-26 13:34:16

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

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

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

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

Strategy is an arena to use your dependency injection skills.
Real ways to implement dependency injection are as follows:-

  1. Events
  2. Configuration files of unity/structure map(or programmatically) etc.
  3. Extension Methods
  4. Abstract Factory pattern
  5. Inversion of control pattern(used by both strategy and Abstract Factory)

There is one thing though that makes strategy stands apart. As you know in Unity when the application starts up all dependencies are set and we can't change it further. But strategy supports runtime dependency change. But WE have to manage/inject the dependency, not the responsibility of Strategy!

Actually strategy does not talk about dependency injection. If needed it can be done through Abstract Factory inside a Strategy pattern. Strategy only talks about creating a family of classes with interface and 'playing' with it. While playing, if we find the classes are in a different tier then we have to inject it ourselves but not the job of Strategy.

娇女薄笑 2024-07-26 13:34:16

如果我们考虑 SOLID 原则 -
我们使用策略模式来实现开闭原则,使用依赖注入来实现依赖倒置原则

If we consider SOLID principles -
We use Strategy Pattern for Open Closed Principle and Dependency Injection for Dependency Inversion Principle

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文