11

I've recently undertaken a refactoring of a medium sized project in Java to go back and add unit tests. When I realized what a pain it was to mock singletons and statics, I finally "got" what I've been reading about them all this time. (I'm one of those people that needs to learn from experience. Oh well.)

So, now that I'm using Spring to create the objects and wire them around, I'm getting rid of static keywords left and right. (If I could potentially want to mock it, it's not really static in the same sense that Math.abs() is, right?) The thing is, I had gotten into the habit of using static to denote that a method didn't rely on any object state. For example:

//Before
import com.thirdparty.ThirdPartyLibrary.Thingy;
public class ThirdPartyLibraryWrapper {
    public static Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
ThirdPartyLibraryWrapper.newThingy(input);
//After
public class ThirdPartyFactory {
    public Thingy newThingy(InputType input) {
         new Thingy.Builder().withInput(input).alwaysFrobnicate().build();
    }
}

//called as...
thirdPartyFactoryInstance.newThingy(input);

So, here's where it gets touchy-feely. I liked the old way because the capital letter told me that, just like Math.sin(x), ThirdPartyLibraryWrapper.newThingy(x) did the same thing the same way every time. There's no object state to change how the object does what I'm asking it to do. Here are some possible answers I'm considering.

  • Nobody else feels this way so there's something wrong with me. Maybe I just haven't really internalized the OO way of doing things! Maybe I'm writing in Java but thinking in FORTRAN or somesuch. (Which would be impressive since I've never written FORTRAN.)
  • Maybe I'm using staticness as a sort of proxy for immutability for the purposes of reasoning about code. That being said, what clues should I have in my code for someone coming along to maintain it to know what's stateful and what's not?
  • Perhaps this should just come for free if I choose good object metaphors? e.g. thingyWrapper doesn't sound like it has state indepdent of the wrapped Thingy which may itself be mutable. Similarly, a thingyFactory sounds like it should be immutable but could have different strategies that are chosen among at creation.

3 Answers 3

12

I liked the old way because the capital letter told me that, just like Math.sin(x), ThirdPartyLibraryWrapper.newThingy(x) did the same thing the same way every time. There's no object state to change how the object does what I'm asking it to do.

That is the right way to do it, yes. That said, static confers no guarantees of immutability or state persistence. That you use it as a suggestion for such guarantees does make sense, but it is by no means assured.

Consider the following:

public static class Logger
{
    public static int LogLevel { get; set; }

    public static void Log(string message, int logLevel)
    {
        if (logLevel >= LogLevel)
        {
            // logs the message, but only if it is important enough.
        }
    }
}

This class not only holds state, but the behavior of the Logger.Log() method changes when that state is changed. It's a perfectly legitimate exercise of a static class pattern, but it has none of the semantic guarantees that you are suggesting.

2
  • I've given you the checkmark because you present a useful counterpoint to the basis of my intuition, but I'd still like to get your thoughts on how I should represent that contract/expectation about lack of state and lack of side effects via coding conventions.
    – leoger
    Dec 14, 2012 at 22:18
  • 1
    Normally such expectations are included in the documentation for the method; the same is true of thread safety. Dec 14, 2012 at 22:23
5

In my opinion what you are saying - all static functions to be nullipotent - is a good OO way of writing code in most cases but I do not believe that when you look at a static function you should automatically assume that it does not have side effects. The situation may be more complex; take for example the function Class.forName(String) which appears to be stateless but it actually loads a class into the memory and eventually instantiates static fields/run static initializer; this is an example of idempotent function (eventual calls after the first one make no difference) but it is not a pure function (it cannot be said that it does not have side-effects). This is also a good choice, but there might be cases in which three distinct calls to the same function yield three different results; for example if you call Thread.currentThread() in a multi-threaded application there is no guarantee you will receive the same thread every time.

That being said, what clues should I have in my code for someone coming along to maintain it to know what's stateful and what's not?

A proper solution is to document (Javadoc for example); also, from the name of the function and from what it does sometimes it can be inferred that the static function is a pure function. For example there is no reason for someone to believe that Assert.assertTrue(boolean) from JUnit would change some state somewhere. On the other hand when a function like System.clearProperty(String) is called, it is pretty obvious that there will be side-effects.

2
  • What's the difference between "nullipotent" and "stateless"?
    – Pacerier
    Mar 10, 2015 at 22:03
  • @Pacerier There shouldn't be any difference.
    – Random42
    Nov 2, 2016 at 15:53
4

That being said, what clues should I have in my code for someone coming along to maintain it to know what's stateful and what's not?

You can make them static. FxCop over in the C# world will push you to make things static that do no reference member variables. This is the correct thing to do (usually).

When I realized what a pain it was to mock singletons and statics, I finally "got" what I've been reading about them all this time.

Moving all of them to instances is perhaps not the right approach. Instead, decouple the dependencies on static methods from the classes that use them. You can test the static methods in isolation, and then replace the dependency in the consumers when they need to be tested.

2
  • Can you paint me an example of "decoupling the dependencies on static methods"? It's not clear to me what you're activating. I've already hidden the implementation details in a static method. At the end of the day, the class that uses it needs to reach that functionality. Are you saying I should create an instance object that has thin wrappers for all of the static methods?
    – leoger
    Dec 11, 2012 at 0:32
  • 1
    @leoger - the class that uses the static method doesn't need to reach the functionality if you're testing that class, otherwise you wouldn't be mocking the static method.
    – Telastyn
    Dec 11, 2012 at 0:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.