3
\$\begingroup\$

When developing with libGDX, you can often read, that your should not use static variables if you plan to eventually release your game on android later.

But while trying to follow that rule, I often ran into problems where I need to use an object only once and keep it accessible from everywhere at the same time.

For example, Im developing on a game which uses an ECS-Approach where different Systems need to use the EventManager-Object to communicate with each other. By default, I would make EventManager static so I can call it from multiple Systems like that EventManager.register(EventType.TYPE_01, this) and EventManager.callEvent(EventType).

How can I avoid using static variables? Is there a best practice design pattern for something like that?

\$\endgroup\$
2
\$\begingroup\$

First off, you might not be able to completely avoid using static variables. You can limit their use, but in some cases they are necessary.

One way to isolate & restrict them is to consider the singleton pattern - it restricts the instantiation to one object & is useful when exactly one object is needed across the system (which is basically what global data does).

According to this SO post, it's what the Google Android team recommended in 2014.

That being said, singletons are controversial, largely for all the same reasons as global data. They make unit testing harder, hide dependencies & allow undisciplined programmers to bypass the black box programming model favored by object oriented paradigms.

Use your singletons wisely:

  • Will every application use the object / data class exactly the same way?
  • Will every application ever need only one instance?
  • Should the clients of the object / data be unaware of the application they are part of?

Ideally, the singleton should only be used if all three of the above conditions are met. Otherwise, it is probably being used as an antipattern.

For use in a game development context, I strongly recommend reading about singletons in Game Programming Patterns. It covers some options such as the Service Locator pattern & piggybacking on existing global data. Depending on your specific situation, they might be better choices.

Again, singletons won't completely eliminate the need to use static variables - they merely provide a design pattern for using them in a disciplined manner.

\$\endgroup\$
  • \$\begingroup\$ How do you write a Singleton which doesn't use a private static variable to keep its instance around? \$\endgroup\$ – Philipp May 24 '18 at 13:12
  • \$\begingroup\$ To the best of my knowledge, you cannot. It was my intent to convey that explicitly in my answer - if I failed to do so, I welcome an edit. \$\endgroup\$ – Pikalek May 24 '18 at 13:47
2
\$\begingroup\$

If you use dependency injection wisely, you will never need to use global variables of any sort.

For communication, for example, you can inject in each system by constructor a mediator that will let the systems communicate between each other.

Let's put it in another way, let's assume that your EventManager is not a static class/singleton, but an instance that you created in your application composition root, that you can pass by constructor in your systems (your systems should also be created in the composition root).

If you create just one instance of the EventManager, there isn't much difference with what you have done. The problem about global variables is the lack of scope, which leads to spaghetti code. This means that you should give a specific responsibility to each EventManager you want to create and inject them only in specific systems. A simple example, just to clarify what I am saying, is that you can have a GUIEventManager that you can inject only in your systems that deals with GUI.

All that said, there are different approaches to solve communication, although all of them rely on injection. For example in Svelto.ECS I use the concept of "sequencer", which is a way to declare that a list of systems is part of a specific communication flow and let each single system to communicate to the next one in the flow.

Communication can happens also through data change. ECS is very prone to this kind of communication, as it can happen through the entity components. Usually it happens through polling, but polling can be awkward. In Svelto.ECS I created a sort of data binding system to communicate through data pushing instead of polling.

\$\endgroup\$

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.