I recently had a rather incoherent rant about why Singleton is an anti-pattern. Let’s say that you decided that static methods needed to be eliminated from your code base. So you embark on the refactoring to end all refactorings. That’s exactly the situation I’m in at the moment. The irony is that the static methods I’m trying to eliminate are calls to StructureMap’s ObjectFactory.GetInstance. Yes, riddled through the code are calls to services. Nothing is denied to any object.
If you start trying to replace static methods with proper instances, you’re going to be asking yourself a lot:
- Why does that object need a connection to that system?
- For crying out loud, why do I need to add an extra constructor parameter to support only one method?
- Why exactly can’t I just store the current user somewhere?
- But now I’ve got X, which has a dependency on Y, and Y has a dependency on X, does this make any sense at all?
- How many methods am I going to have change?
- Does everything have to be an instance method?
- Exactly how many levels do I need to pass this object down?
- Why does this object take a dependency on every service?
- How long is this going to take?
- Is this really worth it?
The answer to the last question is yes.
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. You may even read an article like this and believe that it doesn’t apply to you, because you’ve got your static methods “under control”.
Now, if you’ve never done experienced a code base that’s not riddled with static methods, you’ve never felt what it is to be clean, to be able to analyze your dependencies as easily as being able to examine your constructor, to be able to replace sections of your system and build new systems out of the components you’ve created without worrying about support functionality you’re not actually using. It feels good, but before that, it’s going to feel really bad. I don’t mean really bad in the “non-technical manager who always opposes refactoring” bad, I mean bad as in “This was a huge mistake and I’ve no idea how I’m going to get it working again” bad.
What’s worse, going cold turkey is something you have to do on your own. Solving this one is hard even for other team members to help you. Branching is going to help you this time; it’s best to just have a code freeze. But I figure it won’t hurt to talk, so here’s answers to the questions earlier:
- Almost certainly, because your object design is wrong. Just get it working. We’ll worry about how to fix the design another time.
- Well, you might not have to. I’ll show you how to deal with that later.
- You can, but that “somewhere” is going to be another constructor parameter e.g. ICurrentUserProvider.
- It doesn’t, and it never did. It’s just that static methods allowed you to continue with this state of affairs. You’re going to have to break the circular dependency.
- About 50%. I didn’t say it was going to be easy.
- Yes, it pretty much does. It doesn’t really perform any worse, so don’t worry about it.
- Ridiculous numbers, especially if your object design is wrong or you’re not using a dependency injection tool. Just live with it for now, we’ll come back to this another time.
- It took me three hard slog days with pretty much the entire code base checked out.
- Because it’s assaulting the single responsibility principle with an axe. You’re going to want to restructure this at some point. Not now.