哪个更好:函数重写或传递函数指针进行事件处理
因此,我正在为一个类编写代码,该代码将进入一个库供其他人使用。此类将拦截并处理传入消息(细节并不重要,但它使用的是 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 ownonMessage()
? - Should they pass a pointer to a message handling function to
MessageConsumer
?
What do you think, which option is better and why?
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
纯虚函数允许编译器检查客户端代码是否实现了处理程序。虚拟调度在对象构造后立即处于活动状态,并且查看派生类的人可以准确地推断其处理。处理所需的数据可以方便、清晰地分组到派生类中。工厂仍然可以选择特定的派生类来实例化。
函数指针是运行时状态,因此需要更加小心地及时初始化它们,对它们的设置和错误处理进行可选的运行时检查,并推断哪个集合在程序执行期间有效。随之而来的是在对象的生命周期内更自由地改变它们。
第三种替代方案是模板策略类,或奇怪的重复模板模式,用于在编译时锁定行为。这可能允许内联回调、消除死代码和其他优化。
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.
虚函数或模板化函子是正确的选择。这些方法比函数指针方法提供了更大的灵活性和更松散的耦合。
为了说明这一点,函数指针方法可以用前两个方法包装,但反之则不然。
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.
在一种方法中,允许客户端注册回调,然后 MessageConsumer 调用注册的回调。这有点像观察者/广播设计模式。
第二种方法,客户端必须继承并重写 MessageConsumer ,类似于策略设计模式。
基本设计目标建议使用最弱的关系来促进松耦合。由于与简单关联相比,继承是一种更强的关系,因此其他所有内容都与方法 1 相同是首选。
摘自 Herb 的文章
,除非清楚地了解总体设计限制,否则很难发表评论。
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
But as James points out, it is tough to comment unless the overall design constraints are known clearly.
继承将使您的库更加面向对象友好,并且可以提高可读性。但实际上,选择大致相同,因为编译器将检查用户是否提供了该函数(假设您在基类中声明了一个纯虚拟处理程序),并且底层机制将通过指针完成(无论如何,虚拟表)继承的情况)。
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).