哪个更好:函数重写或传递函数指针进行事件处理

发布于 2024-10-01 20:59:39 字数 614 浏览 0 评论 0原文

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

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

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

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

有两个选择:

  • 他们是否应该从 MessageConsumer 继承一个新类并编写自己的 onMessage()
  • 他们应该将消息处理函数的指针传递给 MessageConsumer 吗?

您认为哪种选择更好,为什么?

谢谢!

So, I'm writing code for a class that will go into a library that will be used by others. This class will intercept and process incoming messages (details are not important but it's using the activemq-cpp library). The outline of this consumer class is

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

where runConsumer() sets up the connection and starts listening and onMessage() is called when a message is received.

My questions is this: People who'll use this code will each have their own way of processing the different messages. How can I keep MessageConsumer generic but offer this flexibility, while keeping their code simple?

Two options:

  • Should they inherit a new class from MessageConsumer and write their own onMessage()?
  • Should they pass a pointer to a message handling function to MessageConsumer?

What do you think, which option is better and why?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

め七分饶幸 2024-10-08 20:59:43

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

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

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

Pure virtual functions allow the compiler to check that the client code implements the handler. Virtual dispatch is active immediately after an object is constructed, and someone looking at the derived class can reason accurately about its handling. Data needed for the handling can be conveniently and clearly grouped into the derived class. Factories can still select a particular derived class to instantiate.

Function pointers are run-time state, so there's a little more care needed to initialise them in a timely fashion, for optional run-time checks on their being set and error handling, and to reason about which set is in effect during program execution. With that comes more freedom to vary them within the lifetime of the object.

A third alternative is a template policy class, or the Curiously Recurring Template Pattern, to lock in the behaviours at compile time. This potentially allows inlining of callbacks, dead-code elimination and other optimisations.

挽容 2024-10-08 20:59:43

虚函数或模板化函子是正确的选择。这些方法比函数指针方法提供了更大的灵活性和更松散的耦合。

为了说明这一点,函数指针方法可以用前两个方法包装,但反之则不然。

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() {...}
}

virtual function or tepmlated functor are the way to go. These approaches give greater flexibility ad looser coupling than function pointer one.

To illustrate that - function pointer approach can be wrapped with first two, but not vice-versa.

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() {...}
}
忆梦 2024-10-08 20:59:42

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

第二种方法,客户端必须继承并重写 MessageConsumer ,类似于策略设计模式。

基本设计目标建议使用最弱的关系来促进松耦合。由于与简单关联相比,继承是一种更强的关系,因此其他所有内容都与方法 1 相同是首选。

摘自 Herb 的文章

“继承经常被过度使用,甚至
由经验丰富的开发人员。总是
最小化耦合:如果一个类
关系可以用更多的形式来表达
比一种方法,使用最弱的
务实的关系。给定
该继承几乎是
你能表达的最强烈的关系
在C++中(仅次于友谊),
只有在真正合适的时候
没有同等较弱的
替代方案。”

,除非清楚地了解总体设计限制,否则很难发表评论。

In one approach, clients are allowed to register a callback and then the MessageConsumer invokes the registered callback. This is something like an observer/broadcast design pattern.

The second approach, where clients have to inherit and override MessageConsumer would be something like Strategy design pattern.

Basic design goals suggest to use the weakest relationship to promote loose coupling. Since inhertiance is a stronger relationship as compared to a simple association, everything else being the same Approach 1 is preferred.

From Herb's article

"Inheritance is often overused, even
by experienced developers. Always
minimize coupling: If a class
relationship can be expressed in more
than one way, use the weakest
relationship that's practical. Given
that inheritance is nearly the
strongest relationship you can express
in C++ (second only to friendship),
it's only really appropriate when
there is no equivalent weaker
alternative."

But as James points out, it is tough to comment unless the overall design constraints are known clearly.

你的心境我的脸 2024-10-08 20:59:42

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

Inheritance will make your library more OO-friendly and may improve readability. But really, the choices are about the same since the compiler will check that the user has supplied the function (assuming you declare a pure virtual handler in the base class), and the underlying mechanism will be accomplished via a pointer anyway (virtual table in the case of inheritance).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文