Static Methods: Time to Hit Rock Bottom
A Tragic Story
It starts out innocently enough. You’re out at a party with some friends, and some people you want to impress are there. Everyone is having a good time, but you notice that some of the ‘cool’ kids are periodically going into a room and coming out giggling. Not wanting to be left out, you ask what’s going on, and are told that it’s none of your business. You have some concept that they’re doing something wrong — something forbidden — and you see what a mystique it creates. Later on, you ask around subtly and figure out what’s going on, and the next time you’re at a party, you know the right things to say to get an invitation to that coveted side room. You make your way in nervously, and there, you are introduced to the exhilarating euphoria of casting aside all thoughts of dependency and responsibility and writing a static method. Now, you’re one of the cool kids.
It’s no problem at first. You just write static methods on the weekends sometimes because it feels good to allow all your classes to access some service without passing an instance around. It’s not as if all of your code is procedural, by any stretch. And, yeah, sometimes you get a little weird behavior or hard to track down defects, but you’ve got it under control. Some of your less cool friends tell you that maybe you should think about modeling those behaviors with instances, but you tell them to mind their own business — they wouldn’t understand.
Static methods are just a way of blowing off steam and dealing with stress. When you’ve got project managers riding you to get things done, deadlines to meet and lots of things on your plate, it’s really gratifying to write a static method or two. They’re so easy, so efficient. It helps you get through the day.
But then, something weird starts to happen. They’re not helping you feel good anymore — they’re not cleaning up your code or making it more efficient. You need them just to get by. You have 50 classes that all depend on some static class with static variables, and they need to retrieve and set those variables in just the right order. It’s a mess, and it’s impossible to figure out why you’ve got this stupid defect that the stupid user shouldn’t care about anyway, that only occurs on Windows 7 when two specific screens are showing, the database connection is closing and a window was opened previously. If only you didn’t have all of these stupid global variables in your static classes, this wouldn’t be a problem. If only you’d listened to your friends. The only way out of this static nightmare for even a few minutes is… another static global flag.
Okay, okay, you admit it. This has gotten a little out of hand. So, you go to the project managers and tell them that the code can’t be fixed. It’s time for version 2.0. You’re going to rewrite everything with a much better design, and this time, you swear, you’re only going to use static methods in moderation. You’ll be a lot more responsible with them this time around. You’ve learned your lesson. Yessiree, this time, everything’s going to be different.
But it’s not. You need to hit rock bottom. Only then can you find your way back.
Static Methods are Like an Addiction
It never seems to fail in a code base of decent size. Poke around the portions of the code that tend to cause the most issues and be hardest to reason about, and the ‘static’ keyword will tend to abound. The worse it is, the more of them you’ll see. People know that global variables are bad, and it is becoming widely accepted that singletons tend to be problematic and abused (if one doesn’t believe use counts as abuse). People generally concur that static state is problematic. And yet, it keeps on keeping on. More is introduced every day. Here are some observations that I’ve made about the use of statics in code:
- Most people rationalize their use.
- Time constraints and stress are often cited as justifications.
- Promises to do something about them at a nebulous future date abound.
- People continue using them in spite of being aware of demonstrable, negative effect.
- The ‘solution’ for the problems they’ve caused is often using more of them.
- People get defensive when asked about them.
One blogger that I follow, Julian Birch, compares them to crack in a post entitled “Going Cold Turkey on Static Methods”:
Static methods are like crack, they’re convenient, they solve your problems quickly and you quickly become dependent upon them. You’re so dependent you don’t even know you’ve got a problem.
I hadn’t seen this particular post when I started mine, but it was nice to google around a bit and see that I’m not the only one to whom this particular metaphor had occurred.
Emerging from the Metaphor
The metaphor here is just that – a literary device intended for illustrative effect in demonstrating a point. I’m not actually suggesting that static methods will ruin your life, and I’m not attempting to trivialize the struggles that people have with actual addiction. I’m posting this because of my observation of the tendency of static methods in a code base to become a real, huge problem, that people immersed in the code base don’t see, but outside observers are immediately alarmed by.
Static state/methods in a code base generally represent a tradeoff, but a subtly different one, in my opinion, than their advocates would have you believe. They claim a tradeoff between complex instantiation logic and complex runtime logic. I claim a tradeoff of a little extra reasoning up front for an avalanche of future problems. To me, introducing a bunch of static functionality into a code base is like saying “I’m making a conscious choice to avoid all the effort needed to plug in my sump pump, and if my basement floods with sewage, I’m fine with that.” What?!? Seriously?!?
In a post with a different device theme, the pun, John Sonmez says:
I don’t like static methods. They make me cringe. In the universe of OO static methods are anti-matter.
He goes on to talk about specific issues with the concept of static methods, including:
- A tendency to violate the SRP since they are methods on a class that have nothing to do with the state of that class.
- They are global procedures (as in procedural code) that are categorically not object oriented programming.
- They kill testability of code bases. Misko Hevery agrees.
- They are not polymorphic in that they cannot inherit, be inherited, or implement interfaces.
- They force programmers to know more things up front.
I would add to this list:
- They hide dependencies and allow an illusion of simplicity in APIs to be created (they cause your API to ‘lie’.)
- They make runtime reasoning about the code very difficult when they encapsulate state.
- Simply by existing, they tend to promote global flags if there is a need to extend functionality and not break existing clients.
- Static classes tend naturally to balloon in size, since their methods can’t be extended.
- They increase potential dependencies in your code base by a factor of order combinatorial.
- They cast temporal couplings in stone within your code.
If you’ve been reading this and not nodding along, you’ve probably experienced some indignation. Please don’t take this as an assault on the choices you’ve made as a programmer. I’m talking from experience here. Ben Franklin famously said “Experience is the best teacher, but a fool will learn from no other.” I am that fool. I’ve struggled with ugly messes that I (and others) have created via the static concept, and I’ve hit that rock bottom state.
The transition away isn’t as bad as you think. I’ve now regularly been creating entire programs and applications that have no static methods. Zip, zero. It isn’t impossible. Heck, it isn’t even hard. It just requires up front reasoning about your dependency graph, but that reasoning is a skill that sharpens with practice. Like unit testing or TDD, it’s a skill that seems a little unnatural, especially if you started in a procedural language, but it does come, and faster than you think. Before you know it, you’ll be loving your static-free existence and offering counseling to those who are walking your current path through a static nightmare.
This is by far the best metaphor I’ve ever encountered for the use of static methods. Outstanding!
I’m glad you liked it — thanks for the feedback and for reading. 🙂
I love using static methods in moderation. If I know a method does not depend on an object’s fields, I’d rather make it static. Then you know it only depends on its parameters. I’d say they actually force you to know less up front, they make dependencies more clear, and they help you avoid excess state encapsulation. You’ll need a little more evidence than, “I’m talking from experience here,” to convince me they are always bad. But what do I know, right?
Hi Josh, Thanks for reading and your feedback. First, the line about me speaking from experience was not intended to convince anyone to do anything — that was more to say that my opinion on statics was based on my own experience rather than dogmatic adherence to a principle or an idea I’d read somewhere. The evidence was intended to be in the “rationale” section. As to your own use of static as you describe, I agree with the sentiment that if a method performs no operation on the instance fields of a class then distinguishing it as static helps… Read more »
You are correct in what you say that when you start to use static methods you will have a hard time to stop using them, I am in that situation.
Currently I use them quite frequently for the following:
– Factory methods
– Wrappers for stored procedures, one example is when fetching a array of User objects from the DB. I don’t think I would like to create some kind of custom “UserCollection” object for this just to get rid of the static method. But I am open for suggestions.
Hi Robert, Thanks for the comment. If you’re interested in eliminating static implementations for static factory methods, you could always use an instance factory method or the Abstract Factory Pattern. (I’ve also posted about that here). Another approach is to use an IoC container, which will create a scenario where you actually push the object instantiation out of your code altogether. With regards to database access, I usually handle this with an instance-based data access layer. There are also proponents of the onion architecture who I imagine would also favor an instance based approach, since that’s all about isolation and… Read more »