1

我的产品有多个组件,其中大多数是用 C++ 编写的,但有一个是用 C 编写的。我们经常遇到这样的场景,即一条信息通过 IPC 流经每个组件。

我们使用结构定义这些消息,以便我们可以将它们打包成消息并通过消息队列发送。这些结构仅用于“传输”目的,并且以仅用于该目的的方式编写。我遇到的问题是:程序员坚持使用该结构并将其用作信息的长期容器。

在我看来,这是一个问题,因为:

1)如果我们改变传输结构,他们所有的代码都会被破坏。这里应该有封装,这样我们就不会遇到这种情况。

2) 消息结构非常笨拙,仅设计用于传输信息……这个结构似乎也不太可能碰巧成为这些其他组件访问此数据(长期)的最方便形式。

我的问题是:如何以编程方式防止这种误用?我想强制这些结构只能用于运输。

编辑:我会尽力在这里提供一个例子:

struct smallElement {
    int id;
    int basicFoo;
};

struct mediumElement {
    int id;
    int basicBar;
    int numSmallElements;
    struct smallElement smallElements[MAX_NUM_SMALL];
};

struct largeElement {
    int id;
    int basicBaz;
    int numMediumElements;
    struct mediumElement[MAX_NUM_MEDIUM];
};

其效果是人们只是坚持“大元素”,而不是从大元素中提取他们需要的数据并将其放入满足他们需求的类中。

4

5 回答 5

2

The programmers are doing this because its the easiest path of least resistance to getting the functionality they want. It may be easier for them to access the data if it were in a class with proper accessors, but then they'd have to write that class and write conversion functions.

Take advantage of their laziness and make the easiest path for them be to do the right thing. For each message struct you creat, create a corresponding class for storing and accessing the data using a nice interface with conversion methods to make it a one liner for them to put the message into the class. Since the class would have nicer accessor methods, it would be easier for them to use it than to do the wrong thing. eg:

msg_struct inputStruct = receiveMsg();
MsgClass msg(inputStruct);
msg.doSomething()
...
msg_struct outputStruct = msg.toStruct();

Rather than find ways to force them to not take the easy way out, make the way you want them to use the code the easiest way. The fact that multiple programmers are using this antipattern, makes me think there is a piece missing to the library that should be provided by the library to accomodate this. You are pushing the creation of this necessary component back on the users of the code, and not likeing the solutions they come up with.

于 2011-03-16T18:48:29.733 回答
2

当我定义消息结构(在 C++ 中,在 C 中无效)时,我确保:

  1. 消息对象是可复制的
  2. 消息对象必须只构建一次
  3. 消息对象构造后不能更改

我不确定这些消息是否仍然是 pod,但我想从内存的角度来看它是等效的。

为实现这一目标要做的事情:

  1. 有一个独特的构造函数来设置每个成员
  2. 将所有成员设为私密
  3. 有 const 成员访问器

例如你可以有这个:

struct Message
{
   int id;
   long data;
   Info info;
};

那么你应该有这个:

class Message // struct or whatever, just make sure public/private are correctly set
{
public:
   Message( int id, long data, long info ) : m_id( id ), m_data( data ), m_info( info ) {}

   int id() const { return m_id; }
   long data() const { return m_data; }
   Info info() const { return m_info; }

private:

   int m_id;
   long m_data;
   Info m_info;

};

现在,用户将能够构建消息并从中读取,但不能长时间更改它,使其无法用作数据容器。但是,他们可以存储一条消息,但以后无法更改它,因此它仅对记忆有用。


或者....您可以使用“黑匣子”

  1. 如果还没有,请在库中分离消息层。
  2. 客户端代码根本不应该暴露给消息结构定义。所以不要提供标题,或隐藏它们或其他东西。
  3. 现在,提供发送消息的函数,它将(内部)构建消息并发送它们。这甚至有助于阅读代码。
  4. 接收消息时,提供通知客户端代码的方法。但是不要直接提供留言!!!只需将它们保存在您的库中的某个地方(可能是暂时的或使用终身规则或其他东西),可能是某种管理器,无论如何,但一定要把它们放在黑盒子里。只需提供一种消息标识符。
  5. 提供从消息中获取信息而不暴露结构的函数。要实现这一点,您有几种方法。在这种情况下,我会提供收集在命名空间中的函数。这些函数会询问消息标识符作为第一个参数,并且只会从消息中返回一个数据(如果需要,这可能是一个完整的对象)。

这样,用户就不能将结构用作数据容器,因为他们没有自己的定义。他们只能访问数据。

这样做有两个问题:明显的性能成本,并且编写和更改显然更重。也许使用一些代码生成器会更好。Google Protobuf 在这个领域充满了好主意。


但最好的方法是让他们明白为什么他们的做事方式迟早会被打破。

于 2011-03-16T18:31:29.840 回答
1

您可以根据 const 引用来实现它们,以便服务器端构造传输结构,但客户端使用只允许对它们具有 const 引用,并且不能实际实例化或构造它们以强制执行您想要的用法。

不幸的是,如果没有您的消息、包装、正确使用和错误使用的代码片段,我无法真正提供有关如何在您的情况下实现此功能的更多详细信息,但我们在数据模型中使用类似的东西来防止不当使用。我还导出并提供模板存储类,以便在他们确实想要存储检索到的数据时,从消息中轻松填充供客户使用。

于 2011-03-16T18:10:45.337 回答
0

我不能说比这更简单:使用协议缓冲区。从长远来看,这将使您的生活变得更加轻松。

于 2011-09-02T20:32:49.440 回答
0

通常将传输消息定义为结构是一个坏主意。最好为它定义“正常”(对程序员有用)结构和序列化器/反序列化器。要自动化序列化器/反序列化器编码,可以在单独的文件中使用宏定义结构并自动生成 typedef 结构和序列化器/反序列化器(增强预处理器库也可能有所帮助)

于 2011-03-16T18:31:32.213 回答