具有许多派生类的可重用类的良好设计

Good design for a reusable class with many derived classes

本文关键字:许多 派生      更新时间:2023-10-16

我有一个类,它将作为(许多(其他类的基类。每个派生类的逻辑都围绕一个函数略有变化,该函数本身将是一组外部函数中的一个。我的目标是拥有高效、清晰的东西,并将为每个新的派生类带来最少的额外代码:

以下是我的想法:

// ctor omitted for brevity
class Base
{
public:
void process(batch_t &batch)
{
if (previous) previous->process(batch);
pre_process(batch);
proc.process(batch);
post_process(batch);
}
protected:
// no op unless overridden
virtual void pre_process(batch_t &batch) {}
virtual void post_process(batch_t &batch) {}
Processor proc;
Base* previous;
}
  • 暴露遵循集合模式的"process"函数
  • 函数的核心逻辑由类"Processor"中的一个drop-in定义
  • 允许通过两个虚拟函数修改此模式,这两个函数定义了在调用Processor::proc之前/之后完成的额外工作
  • 有时,这个对象有一个指向另一个对象的句柄,该句柄必须在它之前做其他事情,为此我有一个指针"previous">

这个设计看起来不错吗?还是有一些我没有考虑到的刺眼的洞?或者在这种情况下还有其他常见的模式吗?

这个设计看起来不错吗?还是有一些我没有考虑到的明显漏洞?或者在这种情况下还有其他常见的模式吗?

在不了解更多目标的情况下,我只能说这似乎很明智。这是非常明智的,事实上,这个成语有一个通用的名字:"非虚拟接口"。如果你在Java领域,也被四人小组描述为"模板方法设计模式"。

您当前正在使用所谓的"模板方法"模式(例如,请参阅此处(。您必须注意,它使用继承通过重写pre_processpost_process方法来实质上修改process(batch)函数的行为。这就产生了强耦合。例如,如果您将基类的子类化为使用特定的pre_process实现,那么在不复制代码的情况下,您就无法在任何其他子类中使用此实现。

我个人会选择"策略"模式(例如,请参阅此处(,它更灵活,更容易重用代码,如下所示:

struct PreProcessor {
virtual void process(batch&) = 0;
};
struct PostProcessor {
virtual void process(batch&) = 0;
};
class Base {
public:
//ctor taking pointers to subclasses of PreProcessor and PostProcessor
void process(batch_t &batch)
{
if (previous) previous->process(batch);
pre_proc->process(batch);
proc.process(batch);
post_proc->process(batch);
}
private:
PreProcessor* pre_proc;
Processor proc;
PostProcessor* post_proc;
Base* previous;
}

现在,您可以创建PreProcessorPostProcessor的子类,可以将它们混合匹配,然后传递给Base类。当然,您可以将同样的方法应用于Processor类。

根据您的信息,我看不出在这里使用继承(一个基类和多个派生类(有任何好处。仅仅因为你有了一对新的前/后处理逻辑,就写一个新的(整个(类不是一个好主意。更不用说,这将使重用这些逻辑变得困难。

我推荐一种更易于组合的设计:

typedef void (*Handle)(batch_t&);
class Foo
{
public:
Foo(Handle pre, Handle post, Foo* previous) :
m_pre(pre),
m_post(post),
m_previous(previous) {}
void process(batch_t& batch)
{
if (m_previous) m_previous->process(batch);
(*m_pre)(batch);
m_proc.process(batch);
(*m_post)(batch);
}
private:
Processor m_proc;
Handle m_pre;
Handle m_post;
Foo* m_previous;
}

通过这种方式,您可以使用所需的任何预/后处理逻辑创建任何自定义的Foo对象。如果创建是重复的,则始终可以将其提取到FooFactory类的createXXX方法中。

p/S:如果你不喜欢函数指针,你可以使用任何表示函数的东西,比如带有一个方法的接口,或者lambda表达式。。。