I noticed that nearly every time I see programmers using static classes in object oriented languages such as C#, they are doing it wrong. The major problems are obviously the global state and the difficulty to swap implementations at runtime or with mocks/stubs during tests.
By curiosity, I’ve looked at a few of my projects selecting ones which are well tested and when I made an effort to actually think about architecture and design. The two usages of static classes I’ve found are:
-
The utility class—something I would rather avoid if I was writing this code today,
-
The application cache: using static class for that is plainly wrong. What if I want to replace it by
MemoryCache
or Redis?
Looking at .NET Framework, I don’t see any example of a valid usage of static classes either. For example:
-
File
static class makes it really painful to switch to alternatives. For example, what if one needs to switch to Isolated Storage or store files directly in memory or needs a provider which can support NTFS transactions or at least be able to deal with paths longer than 259 characters? Having a common interface and multiple implementations appears as easy as usingFile
directly, with the benefit of not having to rewrite most of the code base when requirements change. -
Console
static class makes testing overly complicated. How should I ensure within a unit test that a method outputs a correct data to console? Should I modify the method to send the output to a writeableStream
which is injected at run time? Seems that a non-static console class would be as simple as a static one, just without all the drawbacks of a static class, once again. -
Math
static class is not a good candidate either; what if I need to switch to arbitrary precision arithmetic? Or to some newer, faster implementation? Or to a stub which will tell, during a unit test, that a given method of aMath
class was indeed called during a test?
On Programmer.SE, I’ve read:
-
Don’t Use “Static” in C#? Some answers are quite against static classes. Others assert that “Static methods are fine to use and have a rightful place in programming.”, but don’t bake it with arguments.
-
When to use a Singleton and when to use a static class. Utility classes are mentioned, given that I mentioned above that utility classes are problematic.
-
Why and when should I make a class ‘static’? What is the purpose of ‘static’ keyword on classes? The only valid usage which is mentioned is a container for extension methods. Great.
Aside extension methods, what are the valid uses of static classes, i.e. cases where Dependency Injection or singletons are either impossible or will result in a lower quality design and harder extensibility and maintenance?
14
what if; what if; what if?
YAGNI
Seriously. If someone wants to use different implementations of File
or Math
or Console
then they can go through the pain of abstracting that away. Have you seen/used Java?!? The Java standard libraries are a fine example of what happens when you abstract things for the sake of abstraction. Do I really need to go through 3 steps to build my Calendar object?
All that said, I’m not going to sit here and defend static classes strongly. Static state is solidly evil (even if I still use it occasionally to pool/cache things). Static functions can be evil due to concurrency and testability issues.
But pure static functions are not that terrible. There are elementary things that don’t make sense to abstract out since there’s no sane alternative for the operation. There are implementations of a strategy that can be static because they’re already abstracted via the delegate/functor. And sometimes these functions don’t have an instance class to be associated with, so static classes are fine to help organize them.
Also, extension methods are a practical use, even if they’re not philosophically static classes.
6
In a word Simplicity.
When you decouple too much, you get the hello world from hell.
void main(String[] args) {
TextOutputFactory outputFactory = new TextOutputFactory();
OutputStream stream = outputFactory.CreateStdOutputStream();
Encoding encoding = new EncodingFactory.CreateUtf8Encoding();
stream.Encoding = encoding;
SystemConstant constant = new SystemConstant();
stream.LineEnding = constant.PlatformLineEnding;
stream.FlushOnEachLine = true;
String greeting = new FixedSizeInMemoryString(); // We may have to switch to a file based string later!"
greeting.SetContent("Hello world");
greeting.Initialize();
stream.SendContent(greeting);
stream.EndCurrentLine();
ProcessCompletion completion = new ProcessCompletion();
completion.Status = constant.SuccessStatus;
completion.ExitProgram();
}
But hey, at least there is no XML configuration, which is a nice touch.
static class allow to optimize for the quick and common case. Most the point you mentionned can be very easily solved by introducing your own abstraction over them, or using stuff like Console.Out.
3
The case for static methods: This:
var c = Math.Max(a, Int32.Parse(b));
is much simpler and clearer and more readable than this:
IMathLibrary math = new DefaultMathLibrary();
IIntegerParser integerParser = new DefaultIntegerParser();
var c = math.Max(a, integerParser.Parse(b));
Now add dependency injection to hide the explicit instantiations, and see the one-liner grow into tens or hundreds of lines – all to support the unlikely scenario that you invent your own Max
-implementation which is significantly faster than the one in the framework and you need to be able to transparently switch between the alternative Max
-implementations.
API design is a trade-off between simplicity and flexibility. You can always introduce additional layers of indirection (ad infinitum), but you have to weigh the convenience of the common case against the benefit of additional layers.
For example, you can always write your own instance wrapper around the file system API if you need to be able to transparently switch between storage providers. But without the simpler static methods, everybody would be forced to create an instance every time they had to do a simple thing like saving a file. It would be really a bad design trade-off to optimize for an extremely rare edge case, and making everything more convoluted for the common case. Same with Console
– you can easily create your own wrapper if you need to abstract or mock the console, but quite often you use the console for quick solutions or debugging purposes, so you just want the simplest possible way to output some text.
Furthermore, while “a common interface with multiple implementations” is cool, there is not necessarily a “right” abstraction which unifies all the storage providers you want. You might want to switch between files and isolated storage, but someone else might want to switch between files, database and cookies for storage. The common interface would look different, and it is impossible to predict all possible abstractions users could want. It is much more flexible to provide static methods as the “primitives”, and then let users build their own abstractions and wrappers.
Your Math
example is even more dubious. If you want to change to arbitrary precision math you would presumably need new numeric types. This would be a fundamental change of you whole program, and having to change Math.Max()
to ArbitraryPrecisionMath.Max()
is certainly the least of you worries.
That said, I agree that stateful static classes are in most cases evil.
In general the only places where I would use static classes are those where the class hosts pure functions which don’t cross system boundaries. I realize the latter is redundant as anything that crosses a boundary is likely by intent not pure. I come to this conclusion both from a distrust of global state and from an ease of testing perspective. Even where there is a reasonable expectation that there will be a single implementation for a class which crosses a system boundary (network, file system, I/O device), I choose to use instances rather than static classes because the ability to supply a mock/fake implementation in unit tests is critical in developing fast unit tests with no coupling to actual devices. In cases where the method is a pure function with no coupling to an external system/device I think a static class may be appropriate but only if it doesn’t hinder testability. I find the uses cases to be relatively rare outside existing framework classes. In addition, there may be cases where you have no choice – for example, extension methods in C#.