【发布时间】: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