Some of you may be wondering why I'm writing about fundamentals of mechanical engineering. Actually, This posting is about statics in programming. Other words sometimes used for statics are globals, singletons or pure evil. The goal of this post is to show why you should avoid statics/globals/singletons in your own code, and what you can gain by doing so.
Statics are pretty tempting when first learning Java. Sometimes it's easier to just make something static then figure out how/who needs to receive functionality from a particular class. Singletons are even more dangerous in that they grant the illusion of have a real class while not really buying you an amazing amount of power. (I guess inheritance counts...)
Let's see a typical Java Singleton in action:
package com.blogspot.suereth.statics.suxorz
public class SimpletonSingleton {
private static SimpletonSingleton instance = new SimpletonSingleton();
//This appears like a non-static variable huh? Even looks testable I bet...
private ImportantInterface memberOne;
private SimpletonSingleton() {
memberOne = lookUpImportantInterface(memberOne);
}
//Helper methods, all synchronized of course
...
public static SimpletonSingleton getInstance() {
return instance;
}
}
Rather than diverge into the issues surrounding java singletons and classloaders etc., let's point out a few real issues with this code. I see two major issues
- It appears clever
- It's not
In terms of 1, I've seen and done a lot of "clever" things with singletons. It's amazing how "easy" you can make developing an application. Sure, create a database abstraction that lets you pull in all sorts of things from your database into your application. I remember my days of CORBA in C++ where I created an "OrbAccess" singleton. That was my genius way of not having tons of references passed in the constructors of every class. In C++ the overhead of managing that much information became painful, even with multiple inheritance. (I toyed with the idea of a "ORBHelper" virtual base class that just took in an ORB and gave you helper methods). The problem here is that once you make something a static/global/singleton you lose control of access of the object, but more importantly you lose control of composition of your software.
The second point revolves around this. Object-Oriented software is much more about composition than inheritance. More importantly re-usability and testing become FAR easier with composition.
Look at the above code. Now image how we go about testing it. Here's a few scenarios:
- "There's Only one of these per application so I'll make it a Singleton" Singleton - If only I hadn't seen these in the wild... I wouldn't even mention them. Just because you only want one instance of a class is *NO* reason to make it a formal singleton (like the above). The main problem with this is you lose access to when the object is created, and therefore the *arguments* to the singleton. It is now harder to compose your application. Good Job!
- Data-Layer Access Singleton - We want a singleton that will manage all our access to the database. This way all our JDBC and queries are in one place! great. The "lookUpImportantInterface" method actually pulls in some .properties file and wires in some way of grabbing connections or hibernate sessions, or :::insert framework here:::. Slick huh? Alright, so now let's say I want to test this singleton. I now have to abstract out either the entire JDBC API or some other layer on top of it (ugh). WORSE! If I want to test classes that *use* the singleton, I STILL have to abstract out the entire JDBC API, or bootstrap a database, or some other nonesense. Also, this singleton winds up getting changed any time I want to grab some new type of data out of the database. Isn't this one of the code smells from Martin Fowler's "Refactoring"?
- Factory Singleton - Perhaps this is cheating (since the Data-Layer Access Singleton could be considered a Factory singleton). The basic premise is I want a factory that will generate instances of classes. This way I don't have to remember all the crazy sub-classes, I can just change implementations based on arguments and overloaded methods. YAY! The underlying problem here is how you go about testing *users* of the Factory singleton. You need to be careful about how/when you go about creating and using the objects. If you've designed your software such that a 3rd party does the composition (ala dependency injection), then you can get away with a Factory Singleton AND have testability. If you make the factory calls yourself in a "user" class, you can no longer isolate your testing of that user class, and have to mock whatever framework the Factory Singleton uses when testing its users. This can be difficult, if not impossible if you are not the author of the Factory Singleton.
- Resource Caching Singleton (or the Flyweight Singleton) - Suffers from all the same issues as the Factory Singleton PLUS you have to write massively ugly threading code with statics...
- Event Queue Singleton - As these are usually used in GUI applications where you can only have one thread interacting with widgets, I don't have much negative here. It's already a GUI so testing is hard. Go crazy and write bad code. I'll go work on the server :) As an aside, I think the days of single-threading GUI event loops are coming to an end. Multi-core is going to force their hands. Perhaps when the languages catch up, the core libraries will be next. For other uses, I would rather see instantiations of event queues that get passed into objects than singletons personally.
- X-Pool Singleton - Seems great right? Need a socket, grab one from the pool. Need an inflatable alligator? It's in the pool. Anyone can access the pool, but it enforces fairness right? Oh... you mean it doesn't know who's using it? Well, I'll just have my class call the "get_low_priority" method. Wait, nevermind, *my* class is important. *YOU* call the low priority method. How does the pool get instantiated? OH, it needs to know how to start itself. What about testing? Well I'll need to have a mock-configuration for every test I run on users of the pool... Not so fun anymore... MAYBE I'll just make "get_object" interface and pass it in to the constructor of my object. That way I can change how it works when I compose my application (for testing or production or otherwise).
- Utility Singleton - So this is perhaps the most useful of singletons. What you're actually doing is creating a bunch of pure functions. In C++ you wouldn't even need to place them in a class, just a namespace. As long as the functions are pure (no mutating state), This singleton becomes easily testable.
As you can see, although there are some *ok* usages of singletons, the vast majority of usages detract from locality of code and testability. I've worked on a lot of projects where automated unit testing is thrown out, not because there's no time, but because the design/architecture of the code makes automated unit testing hard/costly. My argument is that (at least in modern languages), there's no reason to throw testability out for these static/singleton usage patterns. I can use this newfangled "Dependency Injection" technique and *still* have slick code.
Am I interfacing with a JmsQueue? Make a simple abstraction:
interface JmsQueue {
public void sendMessage(Serializable msg) throws CommunicationError, FatalError;
public void addMessageListener(MessageListener ml);
public void addErrorHandler(KeepAliveErrorHandler el);
}
Now I can pass a mock instance of this interface into all my classes that send JMS Messages. I can even make sure the messages sent are correct using my mock. I can even test communication errors using my mock, and make sure my business logic is designed to handle it.
For JMS Receivers implementing the MessageListener interface I should be able to test that interface directly.
DON'T Fall into the trap of wiring "Listener" classes into the "Listened" objects during the "Listener" class's constructor. Although it may seem clever, it means that you now need to abstract out the *entire* "listened" object, instead of just calling methods on the listener class. It's better to give yourself the freedom of composition for later enhancements. What if you wanted to load-balance your listeners? What if you wanted to consolidate several listeners?
In conlusion... Avoid singletons if possible. Save "wiring" your classes until later. You can get something working quicker if you can test it out IMMEDIATELY vs. waiting for your infrastructure/architecture to fall in place. Think of coding as two phases:
- Single Class Design, Implementation and Testing
- Composition/Architecture of classes and Integration Testing
Also keep this in mind. Dependency injection does not mean Spring or Guice or J2EE. Dependency injection can be accomplished via "Composer" classes as I like to call them. These are the "Main" classes of an application, that can just instantiate objects and pass them into constructors. This is the guy who sets up an environment for the classes to run in. Your JUnit tests are 'composer' classes. They set up a much smaller execution environment, but they are setting one up. Your main application could have multiple or one large composer class, or it could hide and use a "generic" one that reads "xml" or "annotations". In any case there *is* a composer class that creates your execution environment for you. Break you application into "users" and "composers" and testing will be much easier on you. Make sure your composer classes don't contain too much logic and you should be on your way to an easier-to-maintain code base.
9 comments:
Wouldn't your "composer" objects end up being singleton? Especially, if you don't construct all your classes up front?
It looks like what you're saying is that singletons are ok if you don't have mutable data in them or make their methods have (uncontrolled) side effects. Isn't that true of much of programming?
You, sir, are a retard.
James -
Composer objects wouldn't necessarily be singletons.
For every class I'm creating, I have at least two environments (or uses): Unit Testing and Production. I would therefore have at least two composer objects: Unit-testing Composer and Production Composer
To really have flexibility in unit testing, you don't want composer objects to be singletons, because you'll be creating different styles of "test environments" to run your class in. E.g. when using JNDI I usually make several "failure" environment to make sure I handle errors.
In terms of production environment, I'm usually using spring. Some implementations would make this be a singleton, but some won't. The important thing here is that your classes shouldn't *care* if it's a singleton. With Spring, that means using ContextAware and being injected with an *interface*. This way you remain testable.
I also want to clarify that by singleton I mean: A class that has only one instance AND clients statically access the class.
Ricky -
That's correct. I'm not completely against singletons, I'm only against them when they hurt testability. Global/static mutable shared data usually hurts localized testability.
The important thing here is that your classes shouldn't *care* if it's a singleton.
So singletons aren't for simpletons? I guess that wouldn't have made for an interesting title of your post then
Clever.
Yeah I tried to use a title to grab people's attention.
A few other opinions for your reading pleasure:
What's so bad about the singleton?
Some really good discussions here: Singleton Considered Harmful?
Very interesting. I liked the last paragraph. I have wondered about that some with Spring, why can't my primary application class (psv main) do that for me? It can in many small cases.
Not sure I agree on the singleton issue. GOF describes the intent of a singleton: "Ensure a class only has one instance, and provide a global point of access to it." (p.127) Their application seems reasonable, but perhaps abused in many places today. Personally, I'm not sure why you equate them with pure evil. Can you elaborate? (Or did I miss something?)
My biggest complaint is that you *lose control* over the construction of the singleton in standard implementations. If you're going by GOF description, then some beans defined in spring *are* singletons *and* you have complete control of their "operational environment" (dependencies). In that case, "singletons" are ok. I'm more complaining about standard implementations of singletons inside Java and C++.
Post a Comment