我研发的产品有多个组件,其中大部分是用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];
};
效果是人们只是坚持使用'largeElement'而不是从largeElement中提取他们需要的数据并将其放入满足其需求的类中。
答案 0 :(得分:2)
当我定义消息结构时(在C ++中,在C中无效)我确保:
我不确定这些消息是否仍然是pod,但我认为从内存的角度来看它是等效的。
要实现这一目标的事情:
例如你可以这样:
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;
};
现在,用户将能够构建消息,从中读取消息,但不能长期更改消息,使其无法用作数据容器。然而,它们可以存储一条消息,但以后无法更改它,因此它仅对内存有用。
或....您可以使用“黑匣子”。
这样,用户就不能将结构体用作数据容器,因为它们没有定义。他们可以访问数据。
这有两个问题:明显的性能成本,写入和更改显然更重。也许使用一些代码生成器会更好。 Google Protobuf在这个领域充满了好主意。
但最好的方法是让他们明白为什么他们的行为方式会很快或晚些时候破裂。
答案 1 :(得分:2)
程序员这样做是因为它是获得所需功能的最简单阻力的最简单途径。如果它们在具有适当访问器的类中,则它们可能更容易访问数据,但是他们必须编写该类并编写转换函数。
利用他们的懒惰,让他们做最简单的道路就是做正确的事。对于您创建的每个消息结构,创建一个相应的类,用于使用带有转换方法的漂亮接口来存储和访问数据,以使它们成为将消息放入类中的一个线程。由于该类具有更好的访问器方法,因此使用它比使用错误的东西更容易。例如:
msg_struct inputStruct = receiveMsg();
MsgClass msg(inputStruct);
msg.doSomething()
...
msg_struct outputStruct = msg.toStruct();
而不是找到迫使他们不采取简单方法的方法,而是让你希望他们以最简单的方式使用代码。多个程序员正在使用这个反模式这一事实让我觉得库中缺少一块应该由库来提供以容纳这个。您正在将这个必要组件的创建推回到代码的用户身上,而不是喜欢他们提出的解决方案。
答案 2 :(得分:1)
您可以根据const引用实现它们,以便服务器端构造传输结构,但客户端使用只允许对它们进行const引用,并且实际上无法实例化或构造它们以强制执行您想要的用法。
不幸的是,如果没有您的消息,包装,正确使用和错误使用的代码片段,我无法真正提供有关如何在您的情况下实现这一点的更多细节,但我们在数据模型中使用类似的东西来防止不当使用。我还导出并提供模板存储类,以便在他们想要存储检索到的数据时,从客户端使用的消息中轻松填充。
答案 3 :(得分:0)
通常将传输消息定义为结构是个坏主意。最好为它定义“normal”(对程序员有用)struct和serializer / deserializer。要自动化序列化器/解串器编码,可以在单独的文件中定义带有宏的结构,并自动生成typedef结构和序列化器/解串器(boost预处理器llibrary也可能有帮助)
答案 4 :(得分:0)
我不能说比这更简单:使用protocol buffers。从长远来看,它会让你的生活变得如此简单。