封装和消息对象

Encapsulation & Message Objects

在通过调解器或观察者模式开发内部消息系统时,封装传递的消息对象的最佳方法是什么?

考虑以下消息对象,它告诉一些服务 运行 一个新的 Job。该消息包含需要 运行.

Job
#define MESSAGE_JOB = 1
class NewJobMessage: public Message
{
    virtual int getType() { return MESSAGE_JOB; }
    Job* m_Job;
}

现在可以在服务的以下 handleMessage 函数中处理消息:

bool JobService::handleMessage( Message* msg )
{
    switch( msg.getType() )
    {
    case MESSAGE_JOB:
        runJob( (dynamic_cast<NewJobMessage*>( msg ))->m_Job );
        return true;
    case default:
        return false;
    }
}

这看起来一切都很好,但 NewJobMessage 必须了解作业 class,即使它从未以任何有意义的方式使用它;只是 运行炫耀而已。

是否最好采用其他方式来避免将消息与数据耦合?我考虑过使用 void* 并转换它,但这看起来很老套,如果其他人要剖析我的代码可能会造成混淆。

这是教科书般的void *案例。 void * 的真正缺点是 运行 时间类型检查,但是您的 getType() 方法很明显表明您已经掉进了那个兔子洞。

为了不混淆,请保持结构简单。

// No need to inherit from this.
struct Message {
  int type;
  void * data;
};

只要您以有条理的方式编码,这就不会造成混淆,未类型化的数据是 standard approach for message queues

您没有提到这些消息是如何传递的,或者它们可以传递多远(通过网络?),但总的来说,我强烈建议不要传递本机对象。这是痛苦和苦难的秘诀。这是一个维护和调试的噩梦,一个安全攻击向量,设计耦合问题,而且当这个系统以后被扩展时它只会变得更糟(因为它总是这样)。

所以有几种方法可以解决这个问题:

  • 使用 CDR 或某种此类方式对信息进行编码以对工作信息进行编码。接收方反转 CDR 以取回信息,即使它不是相同的语言、系统或字节 size/order。编码有点乏味,但在计算或 space 方面并不昂贵。
  • 根据您的偏好和经验以及数据中的信息类型,将请求编组到更高级别的内容,例如 JSON 或 XML。这通常在第三方库的协助下相当容易,但转换 to/from 文本并传输它的成本要高一些。诊断问题和调查运输中的事物也容易得多。

在其他条件相同的情况下,我倾向于 JSON,除非您需要处理和传输这些请求的速度太快,以至于您的处理跟不上。在现代 C++ 库的帮助下处理 JSON.

应该相当容易