C++:我应该使用全局变量还是类成员指针在模块之间进行通信?
在我的项目中,我有多个子系统组织为类。
我需要这些类进行通信(以便能够通过指针访问另一个类),并且我希望以尽可能最好的方式实现这一点。
我在这里基本上看到了三种可能的解决方案:
如果子系统 X 需要访问子系统 Y,则向类 X 添加一个指向 Y 实例的成员变量。当创建 X 时,将指向 Y 的指针传递给它并拥有该成员变量
m_pSystemY
设置。为每个子系统声明一个全局变量
CSystemX * g_SystemX
。它将填充一个指向程序启动时新创建的子系统实例的指针。稍后,您可以从任何地方轻松访问它。创建一个复杂的子系统管理器类。所有子系统都存储在一个数组中。您需要调用一个函数才能访问特定的子系统。
我的问题:
我应该在我的游戏引擎项目中使用以下哪一种解决方案?
你们中有人对这些方法有个人经验吗?
In my project, I have multiple subsystems organized as classes.
I need those classes to communicate (so be able to access the other one via a pointer), and I want to implement this in the best way as possible.
I basically see three possible solutions here:
If subsystem X needs to access subsystem Y, add a member variable to class X pointing to an instance of Y. When X is created, pass to it the pointer to Y and have the member variable
m_pSystemY
set.Declare a global variable
CSystemX * g_SystemX
for every subsystem. It will be filled with a pointer to the freshly created subsystem instance on program start. Later on, you can easily access it from anywhere.Create a sophisticated subsystem manager class. All subsystems are stored in an array. You need to call a function in order to access a specific subsystem.
My questions:
Which one of these solutions should I use for my game engine project?
Does anybody of you have personal experience with any of these methods?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
通过指向其他类的指针公开整个类将在整个系统中产生紧密耦合,从而打破“德米特定律”。您可能可以通过已知的设计模式(例如中介模式)使其变得更好。只是一个想法...
Exposing entire class via a pointer to other classes will create a tight coupling throughout the system, and thus breaks the "law of demeter". You could probably make it better by known design patterns, e.g. mediator pattern. just a thought...
真有趣,我在每个方向都看到一个答案!
无论如何,我建议您按照选项#1(向 X 类添加一个指向 Y 实例的成员变量)进行一些小修正:改用接口。
因此,每个模块都会有一个接口。该接口将具有外部人员完成工作可能需要的方法。其他一切都对他们隐藏。
当模块 A 创建时,如果它需要使用模块 B 的功能,您将执行类似
setModuleBHandler(moduleB)
的操作。这样,您可以更改执行您需要的操作的模块,而调用者不会注意到它(因为接口仍然受到尊重)。
这将使您能够遵循其他良好实践,例如 TDD。
示例:
假设模块 Broker 需要日志记录(并且您有一个专门用于日志记录的模块)。与其声明
public void setLogger(Logger logger)
,不如按如下方式声明:public void setLogger(LoggerInterface logger)
。这几乎是一样的。以前有效的方法仍然有效。不同之处在于,现在您可以使用一些完全不同的 Logger 实现,而不必担心对使用它们的无数模块的影响。
例如,您可以让一个类 TextLogger 实现该接口,另一个 DBLogger 实现相同的接口。您可以互换它们而不会出现任何问题。
注意:使用抽象类将得到几乎相同的结果。
我知道这个小例子并没有正确显示这种选择的全部优点,很多人都能发现它的缺点,但我坚信这是大多数情况下的最佳选择(当然每种情况都是一个)案件)。
How funny, I see one answer in each direction!
Anyway, what I recommend you to do it following option #1 (add a member variable to class X pointing to an instance of Y) with a small correction: use an interface instead.
So, each module will have an interface. This interface will have the methods that people from outside might need to do their job. Everything else is hidden from them.
When module A is created, if it needs to use features of module B, you'll do something like
setModuleBHandler(moduleB)
.This way, you can change the modules that do the things you need without the caller noticing it (because the interface is still honored).
This will allow you to follow other good practices such as TDD.
Example:
Suppose module Broker needs logging (and you have a module dedicated to logging). Instead of declaring
public void setLogger(Logger logger)
, prefer to declare it as follows:public void setLogger(LoggerInterface logger)
.This is almost the same. What worked before will still work. The difference is that now you can use some completely different Logger implementation without having to worry about impacts in the myriad of modules that can be using them.
For instance, you can have a class TextLogger implementing the interface and another DBLogger implementing the same interface. You can interchange them without expecting any problems.
Note: using an abstract class will get you nearly the same results.
I am aware that this small example doesn't properly show the full advantages of this choice and many people will be able to find drawbacks in it, but I strongly believe that this is the best choice for most cases (of course each case is a case).
我根本不会使用全局变量。我将通过初始化函数将指针传递给其他系统。无论谁实例化这些系统并初始化它们,都可以决定如何存储它们,可以是它们自己的全局变量、静态类成员、单例包装器,或者(最合适的)某个统一类(即:CGame 类)的成员。无论哪种方式,在初始化时传递它们都是最模块化、可读性和可维护性最高的,并且不会以任何方式牺牲易用性。
I wouldn't use global variables at all. I would pass pointers to other systems via an initialization function. Whoever is instantiating these system and initializing them can decide how to store them, be that their own global variables, static class members, singleton wrappers, or (most appropriately) a member of some unifying class (IE: class CGame). Either way, passing them in at initialization is the most modular, readible, and maintainable without sacrificing ease of use in any way.
我会选择子系统管理器类。我将使用模板来实现这一点,并确保子系统实现相同的接口(即 C# 术语,不确定 C++ 等效项是什么),以便它们都可以相互通信。
I would go with the subsystem manager class. I would implement this using templating and make sure that the subsystems implement the same interface (that is c# lingo not sure what the c++ equivalent is) so they can all talk to each other.
如果你的类总是只有一个实例,那么你可以将它们视为单例:
If you classes are always just going to have one instance, then you can treat them as singletons: