我做了一件阴暗的事情
(看似)阴暗的东西是否因实际原因而被接受?
首先,我的代码背景。我正在编写2D游戏的图形模块。我的模块包含两个以上的类,但我在这里只提两个:Font和GraphicsRenderer。
Font提供了一个接口,通过它可以加载(和释放)文件,而不是更多。在我的字体标题中,我不希望泄漏任何实现细节,包括我正在使用的第三方库的数据类型。我阻止第三方lib在标题中可见的方式是通过一个不完整的类型(我明白这是标准做法):
class Font
{
private:
struct FontData;
boost::shared_ptr<FontData> data_;
};
GraphicsRenderer是(read:singleton)设备,用于初始化和完成第三方图形库,还用于渲染图形对象(如字体,图像等)。它是单身的原因是因为,正如我所说,该类自动初始化第三方库;它在创建单例对象时执行此操作,并在单例销毁时退出库。
无论如何,为了使GR能够呈现Font,它显然必须能够访问其FontData对象。一种选择是拥有一个公共getter,但这会暴露Font的实现(除了Font和GR之外,其他任何类都不应该关心FontData)。相反,我认为让GR成为Font的朋友更好。
注意:到目前为止,我已经做了两件事,有些人可能会认为这是阴暗的(单身人士和朋友),但这些并不是我想问你的事情。尽管如此,如果您认为我将GR作为单身人士和Font的朋友的理由是错误的,请批评我并提供更好的解决方案。
阴暗的事情。因此GR可以访问Font :: data_虽然是友谊,但是它如何确切地知道FontData是什么(因为它没有在标题中定义,它是一个不完整的类型)?我只会展示包含基本原理的代码和评论......
// =============================================================================
// graphics/font.cpp
// -----------------------------------------------------------------------------
struct Font::FontData
: public sf::Font
{
// Just a synonym of sf::Font
};
// A redefinition of FontData exists in GraphicsRenderer::printText(),
// which will have to be modified as well if this definition is modified.
// (The redefinition is called FontDataSurogate.)
// Why not have FontData defined only once in a separate header:
// If the definition of FontData changes, most likely printText() text will
// have to be altered also regardless. Considering that and also that FontData
// has (and should have) a very simple definition, a separate header was
// considered too much of an overhead and of little practical advantage.
// =============================================================================
// graphics/graphics_renderer.cpp
// -----------------------------------------------------------------------------
void GraphicsRenderer::printText(const Font& fnt /* ... */)
{
struct FontDataSurogate
: public sf::Font {
};
FontDataSurogate* suro = (FontDataSurogate*)fnt.data_.get();
sf::Font& font = (sf::Font)(*suro);
// ...
}
所以这是我想要做的阴暗的事情。基本上我想要的是对我的理由的回顾,所以请告诉我,如果你认为我做了一些可怕的事情,或者如果没有确认我的理由,那么我可以有点确定我正在做正确的事情。 :)(这是我最大的项目,我只是在开始时所以我有点感觉黑暗中的东西。)
没有找到相关结果
已邀请:
4 个回复
踩什不
在不同的文件中出现两次(既不是标题)。当你改变一个而不是另一个时,可能会回来并且很麻烦,并且确保两者相同将很可能是一种痛苦。 为了解决这个问题,我建议将定义放在
和相应的包含(无论库/标题定义
)在一个单独的标题中。从需要使用two3ѭ的两个文件中,包括该定义标题(不是来自任何其他代码文件或标题,只是那两个)。 如果你有一个库的主类声明头,那么在那里放置类的前向声明,并在对象和参数(常规指针或共享指针)中使用指针。 然后,您可以使用
或添加get方法来检索数据,但是通过将类定义移动到其自己的标头,您已经创建了该代码的单个副本,并且具有与其他库连接的单个对象/文件。 编辑: 你在我写这篇文章的时候就这个问题发表了评论,所以我会在你的评论中添加回复。 “太多的开销” - 更多的文档,还有一件事要包括,代码的复杂性增长等等。 不是这样。与现在必须保持相同的两个代码相比,您将拥有一个代码副本。代码以任何一种方式存在,因此需要记录,但您的复杂性和特别是维护得以简化。你确实获得了两个
的陈述,但成本如此之高? “实用性很小” - 每次修改FontData时都必须修改printText(),无论它是否在单独的标题中定义。 优点是重复代码更少,使您(和其他人)更容易维护。在输入数据发生变化时修改功能实际上并不令人惊讶或不寻常。将它移动到另一个标题不会花费你任何东西,但提到的包括。
肺鬼耙扮群
很好,鼓励。有关详细信息,请参阅C ++ FAQ Lite的基本原理:朋友是否违反了封装? 这条线确实很可怕,因为它调用了未定义的行为:
臀博
结构的存在,然后继续在两个位置完全声明它:Font和GraphicsRenderer。 EW。现在你必须手动保持这些二进制兼容。 我确信它有效,但你是对的,它有点阴暗。但是,无论何时我们说这样的事情都是eeevil,我们的意思是避免某种做法,但需要注意的是,它有时会有用。话虽如此,我认为这不是其中之一。 一种技术是颠倒你的处理。而不是将所有逻辑放在GraphicsRenderer中,而是将其中的一些放在Font中。像这样:
这样,Font详细信息保存在Font类中,甚至GraphicsRenderer也不需要知道实现的细节。这也解决了
问题(虽然我认为朋友不是那么糟糕)。 根据代码的布局方式以及代码的执行情况,尝试将其反转可能非常困难。如果是这种情况,只需将
的真实声明移动到其自己的头文件中,并在
和
中使用它。
室邢