所以,我正在为一个将要进入其他人使用的库的类编写代码。该类将拦截并处理传入的消息(详细信息并不重要,但它使用的是activemq-cpp库)。此消费者类的概要是
class MessageConsumer {
...
public:
void runConsumer();
virtual void onMessage(const Message* message);
}
其中runConsumer()
设置连接并开始侦听,并在收到消息时调用onMessage()
。
我的问题是:使用此代码的人将各自拥有处理不同消息的方式。如何保持MessageConsumer
通用但提供这种灵活性,同时保持代码简单?
两个选项:
MessageConsumer
继承新课程并自行编写onMessage()
?MessageConsumer
?您如何看待,哪种选择更好?为什么?
谢谢!
答案 0 :(得分:6)
在一种方法中,允许客户端注册回调,然后MessageConsumer调用注册的回调。这类似于观察者/广播设计模式。
第二种方法,客户端必须继承并覆盖MessageConsumer,就像策略设计模式一样。
基本设计目标建议使用最弱的关系来促进松耦合。由于与简单关联相比,无关紧要是一种更强的关系,所以其他所有方法都是相同的方法1。
“继承经常被滥用,甚至 经验丰富的开发者总是 最小化耦合:如果是一个类 关系可以表达更多 不是一种方式,使用最弱的 这种关系很实际。特定 继承几乎是 你能表达的最强关系 在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() {...}
}