5

I need to represent an abstraction over various parts of the hardware for a game. I'm trying to decouple the code that does things like manage the logic of the game from the code that is API/platform specific or any ugly implementation details.

Like this:

std::unique_ptr<IDevice> device(CreateDevice());

IGraphicsDevice *graphics = device->getGraphicsDevice();
ISoundDevice *sound = device->getSoundDevice();
IWindow *window = device->getWindow();
IJobManager *jobmanager = device->getJobManager();
//etc

One solution I've seen was to simply put all of this as globals and initialize them in main, but I'd like to avoid that if I can because I don't really like dealing with globals (and I find them to be "ugly", for the lack of a better word). I also can't allow more than one instance because it requires initialization of libraries which are globally initialized themselves.

What alternatives to the singleton pattern are there for this?

I know that more than one instance would likely lead to errors, is this a bad design decision? Could I do anything different?

  • 6
    you got to be kidding. "class that cannot have more than one instance" is a singleton – gnat Nov 18 '15 at 11:36
  • @gnat The OP replaces global variables by a quasi-global context device, which is a small step up from the classical singleton pattern (assuming you can create multiple devices, which might contradict the "globally initialized themselves" part, otherwise it's just a singleton). – CodesInChaos Nov 18 '15 at 11:38
  • 1
    @CodesInChaos I also thought so up until I read "can't allow more than one instance", which turned it into classic, standard singleton (context doesn't prohibit more than one instance) – gnat Nov 18 '15 at 11:40
  • 2
    I edited this slightly. I think you are not asking for "how to use a singleton" but more "what are the ways to do what you are trying to do" -- which might not be a singleton. I have clarified your question slightly and voted to reopen. If this changed your intent too much, feel free to edit and clarify further. – enderland Nov 18 '15 at 14:39
  • see also So Singletons are bad, then what? – gnat Dec 9 '16 at 9:32
12

This is called the Singleton Pattern and is considered to be an anti-pattern by many people nowadays, since it hides dependencies and makes your code less maintainable.

The necessity for using the singleton pattern may arise from other bad design decisions, but I cannot judge since I know nothing about your architecture and design.

Anyway, let me give you an example how to get rid of this pattern. In a game there will most likely be some kind of main loop which calls everything else. For simplicity let's assume this main loop is contained in an object, but this is not a must. When you create the object that contains the loop, you pass the objects you created in your example code (or rather references to them) to that object. It's possible either to pass them via an appropriate constructor or via setter methods to mention the most common examples. How you do this in you main loop strongly depends on your class design, but you'd be able to perform the rendering something like this

while(running)
{
    game_state->update();
    renderer->render_state(game_state);
}

The adventages of this approach are that you eliminate the need for global objects or singletons and avoid that your objects know how to create your device objects. The only thing your objects have to know is that there are some device abstractions they can use. They do not have to know anything else.

This approach is called Dependency Injection and is considered to be a very important pattern of modern OO-design.

  • Doesn't dependency injection hide dependencies at least as much (if not more) than a Singleton? – JimmyJames Apr 5 '16 at 16:15
  • 1
    No! Totally the opposite. DI enforce you to highlight what are your dependencies and let the Invoker the work of creating those for you. In addition, improve testability and maintainability. – Miguel Aug 23 '17 at 5:23
-3

I would solve this by passing around the IDevice instance to the instances that need it, and only instantiate the class in the main function. This also makes the dependencies more explicit.

  • 3
    Does this solve the "... can't allow more than one instance" requirement? – JeffO Nov 18 '15 at 13:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.