策略模式和继承的区别

19

对于策略模式继承有相同的概念,因此我可以使用继承来实现策略模式,这听起来比策略模式简单和清晰

策略模式

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

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

继承:

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

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

我的问题是它们之间有什么区别?或者我应该什么时候使用策略模式或者继承

链接:策略模式

4个回答

37

想象一下你设计了一个缓存,这个缓存可以具有以下选项:

  • 淘汰策略(后进先出,先进先出,最近最少使用)
  • 过期策略(读取后,写入后)
  • 最大尺寸(元素数量,内存使用量)

现在,假设你想要让你的缓存用户选择任意这些选项,并且你使用继承。那么你需要创建 3*2*2=12 个不同的类:

  • LifoAfterReadNumberOfElementsBasedCache,
  • LifoAfterReadMemoryBasedCache,
  • 等等。

每个 LifoXxx 类都需要实现相同的算法来实现 LIFO 策略。其他策略也是如此。

使用策略模式来实现,你只需要一个 Cache 类,以及 7 个策略类(每种策略一个),你可以随意组合它们,而且不会有代码重复。

而且,你还可以让你的缓存用户定义自己的策略,与其他策略结合使用,而无需创建大量新的子类。


1
代码编译成功后,我无法理解如何通过在运行时更改接口方法的实现来改变对象的行为。 - Reza Ebrahimi
2
cache.useEvictionStrategy(new LifoStrategy()); - JB Nizet
1
@Reza,还要看一下状态模式,它提供了一个策略思想如何进一步发展的例子。给定一个IStrategy类型的字段,您可以在运行时根据事件或其他逻辑设置不同的值。strategyField = new AStrategy(),strategyField = new BStrategy(),其中AStrategy,BStrategy实现IStrategy。因此,它将在运行时更改行为。但是策略和策略持有者的编译类不会改变。 - Valentin P.

9

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

继承

  • 继承用于实现OOP的所有好处(最终导致更好的代码可读性、可维护性和结构)。
  • 继承用于形成许多其他模式,不仅限于策略模式。
  • 由于继承在编译时具有静态的特性,因此有时会成为限制。

策略模式

  • 策略模式假定具有可替换的不同实现(可以在运行时替换或灵活配置)。因此,您的第一个示例也不是策略模式。
  • 一般来说,将组合(由策略模式实现)作为逻辑重用的方式优于继承。首先,它提供了动态多态性。其次,它具有较少的实现限制,如多类继承等。
  • 从DDD的角度来看,“某些可变算法”对应于策略模式,因此对领域建模产生了实际影响。
  • 策略模式本身作为一种模式,为开发人员之间的交流提供了一种语言(“模式语言”)。

此外,根据您的示例

  • 基本的抽象类和接口具有两种不同的语义,因此您的示例中的继承也具有不同的语义。第一个是关于公共契约的实现。第二个是关于泛化作为通用属性和方法提取。

7
在你的问题中链接的维基百科文章中有答案。
策略模式使用组合而不是继承。在策略模式中,行为被定义为单独的接口和实现这些接口的特定类。这样可以更好地解耦行为和使用行为的类之间的关系。行为可以更改而不会破坏使用它的类,并且类可以通过更改所使用的具体实现来切换行为,而不需要进行任何重大的代码更改。行为也可以在运行时以及设计时更改。
如果您的情况简单而静态,可以使用继承,但使用策略允许您更好地解耦复杂代码,并允许在运行时轻松切换策略的实现。
举个例子可能有所帮助。在this related question的答案中以及其他回答中都有一些很好的例子。
一个非常真实的需要在运行时更改策略的情况是排序。排序顺序可以由策略(通常称为比较器)变化,您可以拥有多个策略的实现和机制,使用户能够更改它们。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接