战略模式与继承的差异

时间:2014-09-07 12:42:21

标签: c++ inheritance design-patterns strategy-pattern object-oriented-analysis

Strategy PatternInheritance有一个相同的概念,因此我可以Strategy Pattern实施Inheritance,听起来更简单,更清洁Strategy Pattern

Startegy Pattern

class IBase
{
public:
    virtual void processAction(void *data) = 0; // pure virtual
}

class Worker: public IBase
{
public:
    virtual void processAction(void *data)
    {
        // define logic
    }
}

Inheritance

class Base
{
public:
    virtual void processAction(void *data) {}
}

class Worker: public Base
{
public:
    virtual void processAction(void *data) override
    {
        // define logic
    }
}

我的问题是它们之间的差异是什么?或何时使用Strategy PatternInheritance

链接:Strategy Pattern

4 个答案:

答案 0 :(得分:20)

想象一下,您设计了一个缓存。缓存可以有关于

的选项
  • 驱逐政策(LIFO,FIFO,LRU)
  • 到期政策(读后,写后)
  • 最大尺寸(元素数量,内存使用量)

现在假设您想让缓存的用户选择这些选项中的任何一个,并使用继承。你需要3 * 2 * 2 = 12个不同的类:

  • LifoAfterReadNumberOfElementsBasedCache,
  • LifoAfterReadMemoryBasedCache,

4个LifoXxx类中的每一个都必须实现相同的算法来实现LIFO策略。与其他人相同。

使用策略模式而不是继承来实现它,并且您将拥有一个Cache类和7个策略类(每个策略一个),您可以根据需要进行组合,而无需重复代码。

你也可以让你的Cache用户定义你自己没有预料到的策略,让他把它与其他策略结合起来,而不需要创建很多新的子类。

答案 1 :(得分:5)

策略是OOP的模式,继承是OOP的原则。策略是使用(或支持)继承(实际上是接口泛化)来实现的。

<强>继承

  • 继承用于实现OOP的所有好处(最终 导致更好的可读性\可维护性\代码结构)。
  • 继承用于形成许多其他模式,而不仅仅是策略
  • 由于其静态编译时性质
  • ,继承有时是一种限制

<强>策略

  • 策略假设有不同的实现,可以在运行时替换(或以灵活的方式配置)。所以,你的第一个例子也不是策略。
  • 通常,作为逻辑重用方式的组合(由Strategy实现)优于继承。首先,它提供了动态多态性。第二,它具有较少的实现限制,如多类继承等。
  • 策略对应于DDD方面的“一些可变算法”,因此对域建模有实际影响。
  • 策略作为模式本身,为开发人员之间的通信提供了一种语言(“模式语言”)。

此外,根据您的示例

  • 基本抽象类和接口有两种不同的语义,因此您的示例中的继承也具有不同的语义。首先是关于公共合同的实施。其次是将概括作为对常见属性和方法的提取。

答案 2 :(得分:4)

答案在您在问题中链接的维基百科文章中。

  

策略模式使用组合而不是继承。在策略模式中,行为被定义为单独的接口和实现这些接口的特定类。这允许在行为和使用该行为的类之间更好地解耦。可以在不破坏使用它的类的情况下更改行为,并且类可以通过更改所使用的特定实现而在不需要任何重大代码更改的情况下在行为之间切换。行为也可以在运行时和设计时更改。

如果您的情况简单且静态,您当然可以使用继承,但使用策略可以让您更好地解耦复杂代码,并允许在策略实现之间进行简单切换,即使在运行时也是如此。

示例可能有所帮助。 this related question的答案以及您问题的其他答案中都有一些好的。

您希望能够在运行时更改策略的非常真实的情况是排序。排序顺序可以通过策略(通常称为比较器)来改变,并且您可以为程序的用户提供多种策略和机制的实现来更改它们。

答案 3 :(得分:1)

如果我们谈论Java,您还可以考虑Effective Java中的“基于继承的组合”方法。这将帮助您进行方法调用,因为继承违反了封装(一般情况下)。通过选择组合,您可以进一步扩展抽象,因为它为您提供了所需的服务实例的基础。