哪个更好:函数覆盖或传递函数指针以进行事件处理

时间:2010-11-10 03:41:57

标签: c++ message-queue listener

所以,我正在为一个将要进入其他人使用的库的类编写代码。该类将拦截并处理传入的消息(详细信息并不重要,但它使用的是activemq-cpp库)。此消费者类的概要是

class MessageConsumer {
    ...
    public:
        void runConsumer();
        virtual void onMessage(const Message* message);
}

其中runConsumer()设置连接并开始侦听,并在收到消息时调用onMessage()

我的问题是:使用此代码的人将各自拥有处理不同消息的方式。如何保持MessageConsumer通用但提供这种灵活性,同时保持代码简单?

两个选项:

  • 他们是否应该从MessageConsumer继承新课程并自行编写onMessage()
  • 他们是否应该将指向消息处理函数的指针传递给MessageConsumer

您如何看待,哪种选择更好?为什么?

谢谢!

4 个答案:

答案 0 :(得分:6)

在一种方法中,允许客户端注册回调,然后MessageConsumer调用注册的回调。这类似于观察者/广播设计模式。

第二种方法,客户端必须继承并覆盖MessageConsumer,就像策略设计模式一样。

基本设计目标建议使用最弱的关系来促进松耦合。由于与简单关联相比,无关紧要是一种更强的关系,所以其他所有方法都是相同的方法1。

来自Herb's article

  

“继承经常被滥用,甚至   经验丰富的开发者总是   最小化耦合:如果是一个类   关系可以表达更多   不是一种方式,使用最弱的   这种关系很实际。特定   继承几乎是   你能表达的最强关系   在C ++中(仅次于友谊),   它只适用于   没有相应的弱者   替代“。

但正如詹姆斯指出的那样,除非明确了解整体设计限制,否则很难发表评论。

答案 1 :(得分:1)

继承将使您的库更加友好,并可提高可读性。但实际上,选择大致相同,因为编译器将检查用户是否提供了该函数(假设您在基类中声明了一个纯虚拟处理程序),并且底层机制无论如何都将通过指针完成(虚拟表在继承的情况)。

答案 2 :(得分:1)

纯虚函数允许编译器检查客户端代码是否实现了处理程序。在构造对象后立即激活虚拟分派,并且查看派生类的人可以准确地推断其处理。处理所需的数据可以方便且清楚地分组到派生类中。工厂仍然可以选择特定的派生类来实例化。

函数指针是运行时状态,因此需要更加小心地及时初始化它们,对它们的设置和错误处理进行可选的运行时检查,并推断在哪个集合生效期间有效程序执行。有了它,就可以更自由地在对象的生命周期内改变它们。

第三种方法是模板策略类或奇怪的重复模板模式,以在编译时锁定行为。这可能允许内联回调,死代码消除和其他优化。

答案 3 :(得分:0)

虚拟功能或tepmlated仿函数是要走的路。与函数指针一相比,这些方法提供了更宽松的耦合灵活性。

为了说明 - 函数指针方法可以用前两个包装,但反之亦然。

void cbFunction();

class Interface {
  virtual void act() =0 ;
};

class CbFuctionWrapper:public Interface {
public:
  virtual void act() {cbFunction()};
};

class AnotherImpl: public Interface {
  Context _c; // You can't pass additional context with usual function without downcasting, but OO is all about that.
public:
  virtual void act() {...}
}