【问题标题】:What is the advantage of Strategy Pattern over explicit named methods?策略模式相对于显式命名方法的优势是什么?
【发布时间】:2014-03-13 13:41:43
【问题描述】:

与在显式方法中实现相比,传递策略以作为方法参数执行有什么优势?例如,考虑这个计算器类:

编辑以包含 IOperation 接口

public class Calculator
{
    public double DoOperation(IOperation operation, double num1, double num2)
    {
        return operation.Execute(num1, num2);
    }
}

public interface IOperation
{
    double Execute(double num1, double num2);
}

public class AddOperation : IOperation
{
    public override double Execute(double num1, double num2)
    {
        return num1 + num2;
    }
}

// Leaving out implementations for SubtractOperation, DivideOperation, and MultiplyOperation

相对于:

public class Calculator
{
    public double Add(double num1, double num2)
    {
        return num1 + num2;
    }

    public double Subtract(double num1, double num2)
    {
        return num1 - num2;
    }

    public double Multiply(double num1, double num2)
    {
        return num1 * num2;
    }

    public double Divide(double num1, double num2)
    {
        return num1 / num2;
    }   
}

使用策略模式,如果需要添加操作,则必须创建并测试一个新类。在第二个示例中,您必须创建并测试一个新方法。对我来说似乎有同样的区别。事实上,我更喜欢显式方法名称版本,因为我认为它更容易理解。

我能想到的策略模式只有一个优势。也就是说,如果您正在开发一个供其他人使用的框架,而您确实无法确定其他人可能会实施什么策略。

在所有其他情况下,始终可以实施有限数量的策略。那么,策略模式与显式方法名称“模式”相比有什么优势?

编辑

感谢 Markus,我可以想到上述模式的另一个优势。在第一个示例中,如果 DoOperation() 方法执行更复杂的算法,并且仅使用该策略来分解出不同的代码,那么这将是 DRY 的示例。而且,如果将不同的代码分解为策略,那么能够单独测试策略和公共部分就会有明显的好处。

【问题讨论】:

  • 这看起来不像我所知道的策略模式(没有接口,没有继承)。我将您的示例称为委托模式。据我所知,策略模式的重点是允许您在从正确的角度看待某个类型的变体时将它们视为相同,因为它们都具有相同的方法。我建议你有 3 种计算器,一种用于 bin,一种用于 Deci,一种用于 Hex。驱动程序不知道他们有哪种计算器,但可以在其中任何一个上运行 Add(op1, op2),并获得特定于实现的结果。
  • 这不是策略模式,应该是:public class Calculator { private IOperationStrategy _innerStrategy; public double DoOperation(double num1, double num2) { return _innerStrategy.Execute(num1, num2); } public void SetOperation(IOperationStrategy strategy) { _innerStrategy = strategy } } 抱歉,在 cmets 中格式丢失了。
  • 好吧,我没有包括 IOperation 接口和具体实现,因为我认为它很清楚。但是,我可以编辑问题以包含它们。那么,您是说策略模式需要 一个私有成员变量来保存策略吗?这似乎是一个微不足道的区别。
  • 这不是策略 - 看起来更像是命令。 en.wikipedia.org/wiki/Command_pattern#Example 优点在en.wikipedia.org/wiki/Command_pattern#Uses模式中说明了
  • @Fuhrmantor 感谢您的链接。他们提供了丰富的信息。然而,关于策略模式的 wiki 文章与我发布的代码几乎相同。我看到的唯一区别是该策略存储为私有成员变量,而不是与方法调用一起传递。与命令模式相比,我发现与 wiki 策略模式有更多相似之处。但是,我可能会误解某些东西——我绝对不是设计模式方面的专家。

标签: oop design-patterns polymorphism strategy-pattern


【解决方案1】:

您的示例很短,它支持这样一种观点,即使用策略与分离方法方法相比并没有太大的价值。然而,在更复杂的情况下,DoOperation 方法将更加复杂,并且仅在其算法中的特定点调用策略。

在这种情况下,您可能希望单独测试上下文算法(在您的情况下为 Calculator 类),而不要将测试与方法中的特定实现混合。该模式使您能够分离这些算法并分别测试它们。如果测试失败,您可以轻松识别导致失败的代码块。如果您遵循分离方法方法,则方法共享位于模式上下文中的一些常见(或更糟:重复)代码的可能性很高。在这种情况下,如果您收到测试失败,则更难识别导致失败的代码部分。

此外,实现策略支持开放-封闭-原则,因为如果添加另一个策略,则无需更改 Context 类。现有的策略也保持不变。

策略模式的另一个优点是您可以在运行时更改策略。通常,当前策略作为上下文的属性发布。如果出于某种原因您决定需要更改 Context 的行为,您只需创建新的 Strategy 并将其分配给 Context 的属性。如果遵循分离方法方法,则在编译时决定要调用哪个方法。当然,您可以添加一些if 语句来对某些条件做出反应。如果使用策略,您可以将另一个策略分配给上下文,而无需使用if 语句。这减少了您需要测试的案例数量。

哪种方法更适合特定情况取决于具体情况,但实施策略绝对有充分的理由。

【讨论】:

  • 感谢您的 cmets。与测试类的新方法相比,能够单独测试单个策略有多大优势?您能否详细说明更改运行时的策略?我想我不知道这到底意味着什么。
  • @jrahhali:我已经更新了我的答案;我希望它会有所帮助。
  • 我只能想到一个可以消除 ifelse 结构的实例:如果您有一个包含项目列表的 ui 小部件(例如下拉列表)。当用户做出选择时,选择的价值可以是一种策略。在所有其他情况下,某些外部代码必须在要使用的不同策略之间做出决定。因此,我仍然看不出决定通过什么策略与决定调用什么方法之间的区别。我确实看到这种模式对于在对象构造上交换不同的实现很有用……只是不是作为方法参数。
  • 我同意你的观点,我的例子是微不足道的,也许当我遇到更复杂的问题时,我会看到真正的好处。
  • @jrahhali:是的,这真的取决于情况。避免 if 语句的另一种方法是将策略配置为在文件中使用并动态创建它。
【解决方案2】:

如果您只打算加/减、乘和除,那么您可能只需要这 4 种方法。

但是如果您想在未来添加新的运算(例如高级微积分运算)怎么办?您或其他人可以创建一个新的操作策略实现来执行该操作并将其传递给您的计算器,而不是添加更多会破坏 Calculator 类的所有现有用户的方法。

这样,计算器类和使用计算器类 API 的任何其他类都不会中断,您可以在未来甚至在运行中添加新操作。

【讨论】:

  • 我没有太多的生产代码经验,但是添加一个新方法会如何破坏计算器的api?如果有我的计算器的用户,并且我添加了新操作,那么在计算器类中添加方法与添加新操作有何不同?
  • 你说得对,我没有看到 Calculator 是一个类。向接口添加方法要困难得多,并且会破坏所有还没有该新方法的实现。但是向类添加方法也不是万无一失的。如果一个子类已经有一个具有足够相似签名的方法,那么如果类似的命名方法不能完全按照新的 Calculator 方法所做的事情,它可能会导致复杂性问题或运行时问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
相关资源
最近更新 更多