The keyword static is somewhat awkward in a pure Oriented-Object world. I would like to expose here the usages of static I came up after 15 years of OO Programming.
The 2 rules of thumb are:
A: Static fields that are not pure value constants should be prohibited.
B: Static classes, that contain only pure functions, that compute outputs
from inputs parameters, are allowed. Personally, I am used to suffix
such class with XXXHelper.
You might not agree with these 2 simple rules so let me justify them.
What’s wrong with static fields that are not pure value constant? The underlying motivation for creating a static field comes from the need for an object that lives during all the program execution lifetime. Generally this is a central object, shared by many methods. So instead of passing the object as a parameter to all these methods, making it global makes it more convenient, easy or readable. I see 3 main problems in this reasoning:
First, everytime I stumbled on a need for an object that lives during all the program execution lifetime I found out sooner or later (generally later) that I was wrong. There is no such thing. For example imagine that your program become hosted as an addin (in VisualStudio for example). Addin can be unload and reloaded. Thus the program execution lifetime become session lifetime. And you certainly don’t want that the global object lives between sessions. The cornerstone here, to get my point, is the concept of session. It is virtually impossible to forecast up-front what will be sessions in which our code will live in the future. But there is a very common example of session: Automatic tests. An automatic test (or a set of automatic tests) is a mini session in itself. Having static mutable fields makes the code much less testable because all these mini-sessions must shared the same mutable state, with the need to initialize/clean up it.
Tests are generally ran sequentially. But what if several sessions are ran in parallel and they all share the same mutable objects. Then you need to synchronize access to the global object. And trust me, synchronizing accesses will be a major pain, not even taken account that this will slow down significantly your program. I wrote an article on Managing states in a multi-threaded environment without the synchronization pain. One of my preferred tip in the article is Thread / Resource affinity: The idea is as simple as it is efficient here also: making sure that an object is always accessed by the same thread. Basically each thread hold its own instance. If N threads holds N states then states must be attached to N objects and states cannot be static. To do so, using the ThreadStaticAttribute on your static field can do the job. But it is much more elegant to create a dedicated object for each thread that holds the complete thread-bounded context.
Finally, the third problem with static fields comes from the fact that this makes the program much less maintainable. Static fields represent global state. A global state can be accessed from anywhere in source code. Thus from anywhere in the source code, in any scope, you can potential create a dependency to the global state. The dependency is generally non-obvious because it is generally indirect (because the static field is generally encapsulated by a static static property). But the result is that accesses to the global state become anarchical. It is virtually impossible to enforce accesses to a global state and you will end up with some sort of side-effect nightmare. Sooner or later, the need to refactor the global state in order to control its usage will urge (this is another empirical fact I learnt the hard way).
A consequence of all this is that singleton class must be prohibited. From what we just saw, the single static field of singleton makes program harder to test, harder to maintain, and singletons foster concurrency problems. In my opinion, the problem with singleton is that it is the easiest OOP design pattern to learn. As a consequence, every OOP beginner knows the singleton and has a tendency to use it. Instead of using singleton, I use what I call Managers. A manager is a purely non-static class especially created in the spirit that only one instance will live at a particular point in time. A manager doesn’t have the dirty static instance field trick. Instead managers are generally referenced by other more high level managers. All this grape of managers represent the context in which the code is running. Manager can also be easily passed as a parameter to methods that needs locally to access the context. Also, manager are generally free to initialize and clean up, this make them convenient for testing.
Concerning the second rule B, yes I love purely static helper classes, that don’t have state. The simple CQL query…
SELECT METHODS WHERE IsStatic
…just showed that 36% of my code resides in static methods. Object-Oriented is a great mean to model a problematic and creates proper abstraction. However, too often classes contain functions that computes well-defined output from well-defined input. These functions have a tendency to be potentially static and to be implemented with a lot of lines of code. Doing so result in fat classes hard to understand and maintain. This is why I prefer to nest this code inside dedicated helper classes. Not only this makes my instance classes smaller and clearer, but also automatic tests for helper classes are easy to write: tests just need to create input, call a static method, and check output done by computation. There is no hidden state to take care of initialize. Ironically, a helper class might encapsulate its own private mini-object model that models its own mini-concern.
I know that some others might see these helper classes like trash code that result from lack of design. I agree, such helper classes could be avoided but for what? For more instance classes whose purpose is not clear and obvious. Indeed, if their underlying purpose was obvious in the first place, then the idea to put the code in a static helper wouldn’t’ have even come to the mind. Keep It Simple Stupid!