策略模式优于显式命名方法的优势是什么?

时间:2014-03-13 13:41:44

标签: oop design-patterns polymorphism strategy-pattern

将策略作为方法参数执行而不是在显式方法中实现的优势是什么?例如,请考虑此计算器类:

已修改为包含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的示例。如果将不同的代码纳入策略中,那么能够单独测试策略和公共部分有明显的好处。

2 个答案:

答案 0 :(得分:1)

您的样本非常短,支持使用策略与单独方法方法相比没有太多价值的感知。但是,在更复杂的情况下,DoOperation方法将更复杂,并且仅在其算法中的特定点调用策略。

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

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

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

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

答案 1 :(得分:0)

如果你只是要加/减,乘以除,那么你可能只有这四种方法。

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

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