4

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?

Jarx
  • 41
  • 2
  • Are all these classes you need reference just singleton classes? i.e Do you just have one global instance for each? – Nick Banks Mar 01 '11 at 23:44
  • It sounds like your first two options increase coupling - which is usually a bad thing. Though you described what you *think* you need, it's quite possible that in order to solve your problem you should redesign *how* you're doing what you're doing – Wayne Werner Mar 01 '11 at 23:50

5 Answers5

2

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...

Viren
  • 2,161
  • 22
  • 27
  • 1
    Exposing only the interface necessary to those classes would be advised. Lessened coupling, more maintainable, and the effort necessary to do so is very minimal. – Sion Sheevok Mar 01 '11 at 23:55
2

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).

Pedro Loureiro
  • 11,436
  • 2
  • 31
  • 37
0

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.

Sion Sheevok
  • 4,057
  • 2
  • 21
  • 37
0

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.

Payson Welch
  • 1,388
  • 2
  • 17
  • 29
  • Interface is a concept. There is no interface keyword in C++, but you can effectively create interfaces by creating a class with a pure virtual destructor (stops the class from being instantiated) and virtual methods. – Sion Sheevok Mar 02 '11 at 00:03
-2

If you classes are always just going to have one instance, then you can treat them as singletons:

class singleton
{
  // Private constructor
  singleton() {}
  // Static instance defaul to null in cpp
  static singleton *instance;

public:

  // Access function (put in cpp)
  static singleton* getInstance()
  {
    if(!instance) instance = new singleton();
    return instance;
  }
};
Nick Banks
  • 4,298
  • 5
  • 39
  • 65
  • There is no need to *forcibly* impose a singleton requirement. There is nothing about the requirements that demands it. The Singleton "design-pattern" is one of the most prevalent misused anti-patterns I've ever seen. – Sion Sheevok Mar 01 '11 at 23:54