Static variables are fine if used for constants, which includes constants whose values are calculated at run time but do not depend on any input. Static variables might also be OK if they are only used within a class.
Once static variables become part of your external data flow, you might run into a number of problems:
Initialization order between multiple classes. If you can't guarantee that this field is initialized before the class can be instantiated, you might potentially crash your application. This is especially dire in C++ where segfaults will ensue.
Testability. With static variables, you can't test one object in isolation. You have to think about the whole group of objects that interact with the static variable.
Coupling. If external code interacts with this static variable, this code is tightly coupled to that class. This makes it harder to evolve the code base over time.
Correctness. If external code sets the variable, they might set nonsense. The correctness of the class now depends on the correctness of all its clients, which is undesirable.
I can see two reasonable solutions to this: Lazy initialization, or instantiation via a factory.
With lazy initialization, the constructor would initialize the value, but only if it is currently uninitialized. How this must be done correctly depends on the language, especially if this initialization must be threadsafe. If I remember correctly, the “lazy singleton initialization dance” would look something like this in Java:
private static volatile Thing variable;
Constructor() {
if (variable == null) {
synchronized(Constructor.class) {
if (variable == null)
variable = initializeVariable();
}
}
}
But don't quote me on it…
With a factory, we can fully avoid global variables. The initialized value now is stored in the factory object, and it is passed to each created instance as a dependency in the constructor. Any code that wants to instantiate objects of your class must be given the factory. The code shouldn't create a new factory each time, because that would recalculate the value again.
private Thing variable;
private Constructor(Thing variable) {
this.variable = variable;
...
}
public static class Factory {
private final Thing variable;
Factory() {
variable = initializeVariable();
}
Constructor create() { return new Constructor(variable); }
}
In Java we'd use a nested public static class Factory
, in C++ probably a friend class Factory
instead. I don't know the appropriate C# idiom.
If you are using a dependency injection framework, explicitly writing the factory can likely be avoided. Instead, you'd declare the expensive to calculate instance field, and wire it up to your framework so that it can be filled in automatically. Then, you create some function that can provide this dependency. The framework probably has some singleton mode so that the function is only invoked once.