Back
*1 Terrible solution, but I had only a few days for finishing my task without having serious understanding of used technology and protocols, so leaning on existing stuff was only way. Back
From time to time I am confronted with a piece of code that contains a lot of static methods in class (or several classes) and I struggle to use it effectively for my needs.
Only a few weeks ago I was using class from internal testing library and I needed to do something unforeseen by its author. It was utility class with many static methods. I have looked at its source finding about 1600 lines of code (and javadoc, so I do not know exact number of executable lines). All of them were quite long and doing real work instead of being a Facade and delegating elsewhere. There were also many private static methods and some of them seemed they could help me if I only could call them. Unfortunately I could not modify library so I tried to talk with its author and explain him that proper OO design would help a lot in accommodating unforeseen usages in the future. Hmm, I have learned from that discussion and follow-up I still need to learn how to sell my ideas in diplomatic way... Then only a few days ago I was handed example code for solving similar problem I have been working at that time. It was rather well factored, responsibilities were clearly separated and everything looked fine. But I could not adapt it easily to my needs, because it contained many static methods. Even worse - static methods invoking static methods from different classes. So the only way I could use knowledge and time invested in that code was to copy&paste it into my namespace instead of linking to that component and plugging in different implementation of parts which were different. *1 When I was thinking about this blog post I remembered similar situation that happened several years ago and that probably caused my strong aversion for static methods. I had been made responsible for maintaining and enhancing piece of code that was completely static. Again, several classes with defined responsibilities consisting only from static methods coupled together in non-extensible way. And my task was to add new report to that: "Here is reporting component, add these new reports. How hard can it be?" Well it was hard. All existing reports were done by copy&pasting from previous report, because it was only way to do that. Fortunately I had enough time to rework it completely, leading to nice extensible solution consisting from considerable less number of lines.Why are static methods bad?
Previous section hinted that static methods prevent future growth of code, because they are too rigid and strongly coupled to other parts (static, of course). That means it is not easy to form different path of execution later while still preserving also old one (to implement unforeseen change). So static methods effectively fight against one of the main reasons why object-oriented languages are powerful. The possibility to extend existing code without modifying it (Open Closed Principle). To make it even worse, once you start making class with static methods, it eventually leads to adding more static methods (because they are called from existing static methods). So at the end of our effort we are left with lot of procedural code, that is:- hard to extend.
- hard to read, because all data are passed as parameters, making intention of invocation less obvious.
- hard to apply when requirement to solve slightly different problem arises.
- updated: they prevent small refactorings, because you cannot easily create attribute to keep some state instead of passing all data as parameters. If you introduced such an attribute you would need to consider thread safety while static methods using only parameters and local variables are easy to reason about. Without refactoring code gets ugly soon.
public void configureComponent(Component configuredComponent) { ComponentUtil.setStrictValidation(configuredComponent, true); ComponentUtil.setCollaborators(configuredComponent, collabCollection); ComponentUtil.restart(configuredComponent); }Previous code shows usage of utility class with static methods. Notice that
configuredComponent
is passed into all methods. It has to be, because utility class does not own Component
and it should not own it, otherwise it would not be thread safe (as you could not configure more components at once).
Now, let's see what would happen if we decided that configuration of component is important enough to deserve its own class with life-cycle (instance created).
public void configureComponent(Component configuredComponent) { ComponentConfigurator configurator = new ComponentConfigurator(configuredComponent); configurator.setStrictValidation(true); configurator.setCollaborators(collabCollection); configurator.restart(); }As you can see "inconvenience" to create instance is quickly replaced with less typing needed in all method invocations. Additional advantages (probably not visible in this simple example) are:
- instance of
ComponentConfigurator
is configuring sole instance of component and it can use attributes to store intermediate state without a need to pass it to caller and expecting it to be passed back. - it is easier to reason about code (what it does, what change is safe, what potential bugs can happen and so on) if it is self-contained and encapsulated.
- it is easier to unit test.
How to avoid them
I am not going to write how to get rid of existing static methods. Just refactor the code until they are replaced with normal methods. Instead, I am going to explain how we can avoid them to happen. The best way how to avoid having many static methods in your code is not to write them in the first place. Any time you are creating new class and it looks like it does not need to have any state avoid temptation to make it static. Any time you think "I will put this code into separate utility class, because it has potential to be reused elsewhere" avoid temptation to make it easy to call. Contrary - make it harder to call by asking to create instance of that class. It gives you much more flexibility, because you can pass it to different parts of application, you can compose it with other objects and do all those nice object-oriented things. But static methods can only sit in its class and hope they will be called ;-) Following example might look as overkill to you now, but later when more responsibilities are added into that class you will be glad you decided to do it "hard way".new SomeUtility().performOperation(data);I am not against static methods completely. For example
Math.abs()
is useful, but only because there is no better place to put it to as primitive types in Java cannot have methods. Later, as the utility class grows you might recognize its true purpose or to find some part of it can be identified as standalone concept in your domain and renamed accordingly (or moved into different class). And of course you might realize later that static method would be useful, because it does not need any attributes (question you should ask is if it really belongs to given class or not). So make it static then. It will be more convenient to call after that. But if you make decision about being static too soon you will end up with unmaintainable code sooner.
Note about class naming
Do not forget that class name suffixesHelper
, Utility
, Manager
and Controller
are by many developers considered problematic, because they are an excuse to collect a lot of functionality that should belong into different classes, leaving them to be pure data structures. It is another example how OO language can be used for procedural programming. Utility
and Helper
fit well into this article as they are also good candidates to consists from static methods!
---*1 Terrible solution, but I had only a few days for finishing my task without having serious understanding of used technology and protocols, so leaning on existing stuff was only way. Back