【问题标题】:C++: Base class calling own virtual function - an anti-pattern?C++:基类调用自己的虚函数——反模式?
【发布时间】:2019-08-02 05:42:58
【问题描述】:

我看到下面常用的模式。我们有一个基类来完成大部分工作,但调用它自己的一个虚拟/纯虚拟函数来完成对每个派生类型都不同的部分工作。一个人为的例子:

struct PacketProcessor {
    virtual void parseEncap(pkt) = 0;
    void process(Pkt pkt)
    {
        parseEncap(pkt);  // Calls its own virtual function to parse the encap
        processFurther(pkt);
        ...
    }
};

我们创建的派生类将覆盖虚函数并提供特定于派生类的功能。

struct EthernetProcessor : public PacketProcessor {
    void parseEncap(Pkt) override { // parse ethernet encap}
};

struct PPPProcessor : public PacketProcessor {
     void parseEncap(Pkt) override { //parse ppp encap }
};

但是我觉得随着时间的推移,基类中越来越多的函数被虚拟化,或者更多的虚拟函数被添加并在随机位置调用,以便为不同的派生类行为腾出空间。

[在现实生活中的代码中,我看到了 add() 和 add_extra() 虚函数 :-)]

随着时间的推移,代码不再具有坚实的结构,因为每种类型的处理方式完全不同。即使公共基类给了一个错误的概念结构。但是是的,它仍然将不同类型的代码分开,而不是 if (type1)/else(type2)。

实现类似功能的另一种方法是将差异抽象到不同的类(层次结构)中,并在其中调用虚函数。这也很常见,例如:

struct Encap {
    virtual void parseEncap(Pkt) = 0;
};

struct EthernetEncap : public Encap {
    void parseEncap(Pkt pkt) {}
};

struct PPPEncap : public Encap {
    void parseEncap(Pkt pkt) {}
};

struct PacketProcessor {
    PacketProcessor(Encap *encap) : m_encap{encap} {}
    void process(Pkt pkt)
    {
       m_encap->parseEncap(pkt);
       processFurther(pkt);
       ...
    }
private:
    EncapPtr m_encap;
};

但是在这种情况下,也可以在 Encap 类中添加一些琐碎的功能。或者有太多像 Encap 这样的组件类提供不同的功能。

但好在 PacketProcessor 将遵循所有类型封装的特定路径。此外,这种方法不如前一种模式灵活,因为我们需要将所有“封装”放入一个非常特定的模型中。

所以我的问题是:

其中一种是反模式,应该避免还是缺点只是缺乏纪律,与遵循的模式无关。

【问题讨论】:

  • 这真的适合你吗?虚拟调度在构造函数中不起作用,因此您永远不会调用派生类函数,只调用基类。
  • fwiw 你在第一个 sn-p 中拥有的基本上是四人组“模板方法模式”(名称中出现“模板”有点不幸,与 c++ 模板无关)
  • 我不会称之为反模式,它就是所谓的template method。当然,您本地项目的可用性可能会有所不同。
  • 模式可能被误用,但它本身并没有错。
  • 我不会称它们为反模式。但是(就像任何给定语言提供的许多工具一样)它们是可以被滥用或误用的东西。

标签: c++ oop design-patterns c++14


【解决方案1】:

该模式称为template method pattern

但我觉得这种模式,随着时间的推移,越来越多 基类中的函数被虚拟化或更多的虚拟函数 在随机位置添加和调用,为不同的派生腾出空间 类行为。

该模式通常应用于框架中,其中基类由框架提供,然后由框架的用户提供实际功能。在这种情况下,基类相当稳定,而用户数量和实现的可变性很大。在这种情况下,该模式做得很好。

您的情况似乎有点不同,因为我知道您可以控制基类和实现。如果这是真的,那么模板方法模式可能不是你真正需要的。

其中任何一个都是反模式,应该避免还是有缺点 只是缺乏纪律,与遵循的模式无关。

说实话,我没有完全理解您的替代方法。但是,我看不到任何根本上的损坏。但是,考虑到如果基础和派生都不受您的控制,那么您的方法与模板方法方法有很大不同。

任何模式都可能被误用和过度使用。此外,没有一种模式是没有选择的。模式的主要好处无非就是它们是模式。他们可以被识别。您的第一个 sn-p 已被多个用户识别为模板方法模式,而第二个,真的没有冒犯,只是另一个类层次结构。

TL;DR 模式不是圣杯。仅仅因为一个模式可以被滥用并不意味着该模式被破坏了。如果您认为变体或完全不同的东西对您更有效,那么请继续。

【讨论】:

  • 第二个似乎是依赖注入的一个例子(en.wikipedia.org/wiki/Dependency_injection
  • @David 哦,对了,在我认为可以通过以更好的方式比较这两种方法来改进答案之前。我想我稍后会编辑。感谢您的链接
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-11
  • 2010-09-28
  • 1970-01-01
  • 2019-01-09
  • 2012-04-01
相关资源
最近更新 更多