C ++:调用自己的虚函数的基类-反模式?

时间:2019-08-01 21:03:56

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

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

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)相对,它仍然将不同类型的代码分隔开。

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

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)提供不同的功能。

但是,好消息是对于所有类型的Encaps,PacketProcessor都将遵循特定的路径。同样,这种方法没有以前的方法灵活,因为我们需要将所有“ Encap”放入一个非常特定的模型中。

所以我的问题是:

是其中一种反模式,应该避免,或者缺点仅仅是缺乏纪律,与随后的模式无关。

1 个答案:

答案 0 :(得分:1)

该模式称为template method pattern

  

但是随着时间的流逝,我越来越喜欢这种模式   基类中的函数得到虚拟化或更多虚拟函数   添加并在随机位置调用以为不同派生空间腾出空间   课堂行为。

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

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

  

是其中一种反模式,应避免使用或出现缺点   只是缺乏纪律,与随后的模式无关。

老实说,我并没有完全理解您的替代方法。但是,我看不到有任何根本性的破坏。但是,请考虑一下,如果基数和派生方法都不在您的控制之下,则您的方法与模板方法有很大不同。

任何模式都可能被滥用和过度使用。而且,没有任何模式没有其他选择。模式的主要好处仅在于它们是模式。它们可以被识别。您的第一个代码片段已被多个用户识别为模板方法模式,而第二个代码(实际上没有冒犯)只是另一个类层次结构。

TL; DR 模式不是圣杯。仅因为可以滥用某个模式并不意味着该模式已损坏。如果您认为变化或完全不同的东西对您更好,那么请继续。