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.
About 2 weeks ago I had discussion with a few colleagues of mine. I have asked them what they think about following Javadoc and how to enhance it.
class Person { // definition of attributes, I am not stating them to // avoid attracting your attention now /** * Gets value of the first name property. * @return first name of person */ public String getFirstName() { return m_FirstName; } }I have provided additional constraints:
- the class is as-is, we do not care about e.g. persistence (to avoid temptation to define maximum size).
- we do not care about constructor (or setter) and what constraints it brings.
- only this getter for Java Bean interests us here as my idea is to represent any simple method in general.
- do not speculate what else could be done in this method, just provide useful Javadoc.
- I have selected Person and first name as widely understood concept to make it harder to provide useful Javadoc (in order to shed more light into this apparently confusing concept ;-) )
- "Maybe format could be described" (there is none in my example).
- "In case of future enhancements (e.g. encryption) it is better to have Javadoc present since beginning, because some developers will not add one when they are modifying it" (I have simplified this suggestion to make it shorter. Well, I am not sure if a group of developers who would not modify existing Javadoc is not bigger...)
Comments are always failures. We must have them because we cannot always figure out how to express ourselves without them, but their use is not a cause for celebration.I am not as strict about this (I am probably not able to express all my ideas cleanly often), but in general I agree that strict asking for Javadoc is healing symptoms instead of real root cause in typical code. Are you not persuaded yet? Well, what do you think about following example describing common usage of Logger?
/** Logger */ private static final Logger LOG = Logger.getLogger(Person.class);While it is probable that there is only one
Person.getFirstName()
in codebase and it deserves some kind of documentation, what should I think about comment describing well known way how logger is used in about 2000 classes I have seen in our project till now?
Conclusion
I wanted to make title of this post attractive... I generally consider Javadoc to be useful. But I wanted to point out that sometimes there are valid reasons to skip it. Unfortunately Checkstyle is not able to identify these situations while people are.A few people asked me when I am going to publish more posts here.
Eventually I will, but my urge to explain things and express my opinions is currently satisfied with my voluntary actions in my work. I had to write several articles where I try my best to express myself to sell my ideas so there is not much left to say at this blog. And it looks like it will be same for some time. It also fulfills my resolution to get better in writing (one reason I started this blog). The only disadvantage of my current way is that I have lost about 1 hour of writing yesterday as I used Back button in browser and our wiki did not help me to locate overwritten stuff. That would not happen on my blog as I could take lost text from log. But it helped me to make it better today :-) On the other hand, I have real feedback there and it is good feeling to see people accepting my ideas and acknowledge my effort. BTW, my writing efforts at work are about unit testing, but I cannot publish any part here due to ownership of stuff produced by me at work...My Dilbert 2.0 book has just arrived :-) Finally I can learn all missing tricks about OOP.
Oh, did you know that all I know about OOP I have learned from Dilbert? If not, you have probably missed this old, but very good article. It was pointed out to me by my experienced former colleague and it really made the difference back then. Now it's pity that Dilbert 2.0 is meant as Xmas present to me from my wonderful fiancée, so I need to wait...Today I have done the first session How to make unit testing better with my colleagues. I am satisfied with my performance, as it was the biggest audience I have ever had. It took me long time till I got enough courage to start - I was thinking about introducing "my tricks" to wider audience of my colleagues since May... (but I've done something in my team sooner).
I was not sure how it will be accepted. Now I know my fears were unfounded. Colleagues appreciated my effort and are happy to continue, they suggested mailing list and wiki to track discussed points. But they might not be aware of fact that they will not be only passive listeners next time :-D If everything goes well, I can imagine that we will get to general topics like design, design principles, clean code and anything else that developers should want to discuss soon. I plan to show TDD soon. I am looking forward to that, but again I am bit afraid that will end up with "20 bored spell checkers". I need to do that interactive as much as possible.