Why globals are (almost always) wrong

Imagine one day you would like to re-use the code of your app, then run your business logic code on a server.
You would like to re-use your existing code.
But since all your client instances would have to share the same global data, stored in static variables, you would be stuck to make the running instances un-coupled.

This just breaks the SOLID principles.
Both the Single responsibility principles, and the Dependency injection, to name the biggest two.
What should be done instead of such deprecated globals is to use true classes (or even better interfaces), then use Inversion Of Control / Dependency Injection.

When globals may be used

If you look at our mORMot source code, you will find some global variables here and there.
You won't find many global variable instances defined in the interface section of our units, but you may find some global variables in the implementation of well-identified shared functions.

I found out the following rules (about singleton potential use) to be of good interest:

To decide whether a class is truly a singleton, you must ask yourself some questions.

  1. Will every application use this class exactly the same way? (exactly is the key word)
  2. Will every application ever need only one instance of this class? (ever and one are the key words)
  3. Should the clients of this class be unaware of the application they are part of?

If you answer yes to all three questions, then you've found a singleton.
The key points here are that a class is only a singleton if all applications treat it exactly the same and if its clients can use the class without an application context.

These are in fact the use cases where we define some global variables.
Even more, we use a GarbageCollector: TObjectList instance, to handle some global variables which would last for the whole application.

Most of those globals are defined at very low level, in SynCommons.pas:

  • For process-wide logging purpose;
  • For RTTI cache;
  • For some class-level VMT tricks
  • For WinAnsi / CurrentAnsi encoding conversion classes;
  • For global class registration, with virtual constructors;
  • For execution context, e.g. the ServiceContextthreadvar.

Note that some of those can be by-passed, and you can use e.g. uncoupled logging, with a private family, if needed.

I just hope this little blog article may help you refine your own coding style, and let your projects be more maintainable.