“any symptom in the source code of a program that possibly indicates a deeper problem.”
static methods allow you to execute code at a “class scope” as opposed to an instance scope like member methods. This means, they rely on class-level variables (if any), parameters passed to the static method, or any other globally accessible data. They are NOT object oriented. An object has state associated with it and can be manipulated only through methods that implement the object’s “behavior.” Static methods do not operate on state, they are not object oriented, and in fact they are procedural.
Is this bad?
No. Although Java is object oriented, there will be times when procedural-like programming in Java is necessary and/or preferred. The real power in any object-oriented language is the ability to closely implement a model of real-life systems in code (see my
post about object-oriented modeling). But even in the most hard-core object models, there will mostly likely be some glue code, or infrastructure code that will be implemented in a procedural style.
So if procedural-like programming in Java isn’t “that bad” and static methods are a form of procedural programming, are static methods bad?
Ahh… the answer isn’t as simple as a “yes”, or a “no” regardless of what you may read on other blogs But I could probably ramble on and on about why it’s really a decision that has to be made in context, so let’s keep this article focused by examining a set of statements that I encountered in
“How to Mock Static Methods” from Michael Minella’s blog:
“Something that has become a fundimental [sic] piece of the language (all you need to do is look at the Apache Commons project to see that) is so bad that it must be avoided at all costs in the name of testing. Gosling (or someone on his team) put it in the language for a reason and to avoid those uses solely because your toolset doesn’t support the testing of it is nonsense. Time to get a new toolset.”
First, I’d like to point out that just because something has become a fundamental piece of a language doesn’t mean it’s “good” or something that should be done. Take a look at
checked exceptions for reference. I recall EJB 1.x and 2.x becoming a “fundamental” piece of Java EE in the past, so look at that for reference too.
Second, although I do agree with Michael in theory that avoiding a particular language feature because your tools don’t support it is silly, his premise is focused on static methods. Avoiding static methods because your tooling doesn’t support them is NOT nonsense at all. In fact, the type of impedance caused by some of the good testing and/or mocking frameworks (of which
Mockito is my favorite
) and static methods can be confidently identified as a code smell. This does not mean we should not do it, but we should exert extra effort to understand why we are doing it and explore alternatives if there are “deeper problems.”
There are at least two types of static methods that I would like to point out don’t usually show much impedance with testing/mocking frameworks. The first type is static methods used as utility methods, as those found in a lot of the
apache commons libraries, or your own internal commons libraries. These are usually routines that are supportive of a particular method’s objective, and mocking/stubbing them out of a unit test wouldn’t make sense. They are part of the implementation, and should be tested as such. The second type is static methods used in place of constructors as Joshua Bloch showed in his book
“Effective Java.” This use of static methods allows you to construct a new object using a method with a very descriptive name, among some other advantages. An offshoot of this second type of static method could include factory methods, but that would depend on context.
The most glaring code smell as a result of static methods and testing-framework impedance arises when a unit relies on a static method that performs logic outside the scope of the responsibility of that unit. In these cases, your testing framework will fight you because you cannot stub/mock the out-of-scope logic because it is “hardcoded” via a static method. This can be considered a “deeper problem” and is the focus of most blogs that tell you not to use static methods because testing becomes abnormally difficult or impossible. Changing the design approach to follow the Dependency Inversion Principle is one alternative. A better understanding of how to test the unit is yet another.
I assert strongly that in the case of static methods, the push-back you may get from your testing frameworks is indicative of a code smell, not that you need to try and find a framework that uses complex trickery with class loader remapping as a solution. One should be poised to evaluate the use and fundamental drawbacks of a particular approach in their design. Michael’s blog entry lends the reader to too easily assume a new tool/framework just because Java supports static methods and your current testing frameworks illuminate an impedance — in this case, the impedance reflects a code smell and some deeper, more critical thinking is required.