The Fact/Intention Gap 8

Posted by Michael Feathers Tue, 21 Oct 2008 16:57:00 GMT

The other day, I was browsing some code with a team and we came across a cluster of static methods. I looked at them and asked whether they were used someplace else without an object reference. I could’ve done a find references in the IDE, but one of the team members had the answer: “No, they’re just used here.”

“Okay, so why are they static?”, I asked.

“Well, the IDE pointed out that they don’t refer to any instance data, so I made them static.”

Technically, there’s nothing wrong with that. I do the same thing sometimes. I make a method static to document its independence of instance data. But, this scenario highlights something important about static and a few other keywords: their uses can be seen as statements of fact or statements of intention. Static can be read as “Hey, this function doesn’t use any instance data, and you should know that.” Or, it can be read as “I am making this static so that it can be used easily anyplace without an instance.” When you’re trying to read a pile of unfamiliar code, it’s nice to know whether you can count on one meaning or the other.

I don’t think I’ve ever heard anyone articulate this directly, but there’s a tendency in a lot of best practice literature to close the gap between fact and intention. Joshua Bloch’s advice to use make fields final whenever you can in Java is a good example. The traditional advice to make methods private whenever they are not used outside of a class is another.

Part of me feels that closing the gap is a good practice but the fact is, there are holes in it. If you are developing a library or a framework, you do have to write code that may not reflect the facts of your code, but rather will reflect the facts of your code and the code that people write to use it or extend it. Beyond that, it might be overly conservative to reduce visibility of things that aren’t used beyond a particular scope. For instance, imagine a method that is used only by other methods of a class. We understand the invariant of the class and we see that the method could be public. We could make it private now, but that means that anyone who wants to make it public later would have to do some re-analysis to arrive at the answer that we have right now.

The Fact/Intention Gap is a very real thing. Whether we know it or not, we confront it every time we try to understand unfamiliar code. I think there’s only one way to solve it, and that’s to try to separate fact from intention in our languages and tooling. Imagine what it would be like if your IDE gave you a visual indicator for all methods which didn’t use instance data. If it did, you could use static on methods only when you want to indicate that the intention is to use them without an instance.

It seems that IDE developers are moving in this direction. I wonder if any language designers will follow suit.

Comments

Leave a response

  1. Avatar
    Corey Haines about 2 hours later:

    Great post, Michael.

    Personally, I’m a huge static hater. Mostly because of the difficulties they bring with dependencies. I much prefer class-methods a la Ruby, which are instance methods of a different object (the class).

    I like the view that static’s signify a lack of reliance on fields. While I’m not a huge fan of mixing data and behavior in my classes, I can see the point there.

  2. Avatar
    Justin Kohnen about 2 hours later:

    Nice post.

    All too often I get caught up with the semantics of the syntax and not how my use of the syntax might be interpreted by later developers. Is my use a statement of fact or a statement of intent?

    You’re post has given me some food for thought.

    Justin

  3. Avatar
    Llewellyn falco about 3 hours later:

    I run across this problem a lot. However, I am a static lover! The thing about making a method static is it clearly states “the logic of this function is self contained” Not only is this self contained, it removes a dependency on the instance of the object. Now, I agree that the inability to override the method is annoying, and wish there was a clean way to avoid that, but compare that to just what the instance object dependency that is NOT USED can mean.

    1) you might need a connection to a database to create the object

    2) you might need a invoke the gui system to call the function (anything on a object that extends jpanel)

    3) you might need a very complicated system to be created (anything on an object in the eclipse editor)

    4) you might need it figure out which constructor to use (any object that doesn’t have a default constructor)

    Also as to intention, static function state the cleanest intention of what the parameters are. take the follow example:

    findFirstDifference()

    int findFirstDifference(String first, String second)

    the second is much clearer on what the parameters are and what the outcome is. In ‘clean code’ bob martin says “the ideal function has zero parameters” but I disagree. I believe the ideal function has only the parameters it actually needs.

    when you say findFirstDifference() you really mean findFirstDifference(this) and it is rare that you actually need all the member variables in your object.

  4. Avatar
    Dean Wampler about 4 hours later:

    When I see a lot of static methods, my first question is whether or not there’s another class in there just crying to be free. Maybe there should really be a singleton instance of that embedded class. Moving back to an instance restores the option of subclassing and overriding later to change behavior.

    That’s not always the case, though. For example, most large applications end up with a “StringHelper” class that provides service methods for strings that you wish were on String itself.

  5. Avatar
    Nevin ":-)" about 12 hours later:

    “We understand the invariant of the class and we see that the method could be public. We could make it private now, but that means that anyone who wants to make it public later would have to do some re-analysis to arrive at the answer that we have right now.”

    Sure, but on the other side of the coin, once it is public not only must all future implementations implement it (or I have to rework all the clients), but it must also maintain that invariant.

    It isn’t overly conservative; rather, it is just a tradeoff.

  6. Avatar
    Esko Luontola 1 day later:

    Every now and then I’m annoyed when I try to extend a Java program, but some of the classes which I need to access are package-private. Sometimes even most of the classes in a package are package-private. Then I end up creating wrapper classes (in the same package or using reflection) just for accessing some of the public or package-private methods in those classes.

  7. Avatar
    Ryan Cammer 3 days later:

    resharper tells you when a method doesn’t refer to instance data, and prompts you to make it static :)

  8. Avatar
    Dmitry Kandalov 4 days later:

    Great post. Now I have a name for the thing I deal with anyway.

    In case of “intentionally static” methods I believe it shouldn’t be hard to do it in IntelliJ with @Static annotation and some inspection which checks that annotation is used correctly.

    Going a bit off topic, I think it may also be interesting to hightlight instance methods calls to see points where object state is used; sometimes I add temporary static modifier to do it.

Comments