In a reaction to the whole fad of using components for everything, the Java folks came up with a back to grass roots movement called Plain Old Java Objects (POJO). That’s all fine and good, but Java’s not the only language with objects. So in this article I’m writing about Plain Old Objects, or POO for short.
Fragile POO and Testing Foo
Writing good unit tests is it’s own challenge. In order to have confidence that you are testing what you believe you are testing, you really need to isolate the code. I don’t know about you, but most objects I write use other objects. In fact, you’ll find it fairly common to see object trees in your application. That’s true if you are creating a web application that manages people’s information or if you are writing games. There’s probably a lot of ways to create the tree structure, but there are a few things that will cause a fragile architecture and easily broken tests.
Let’s say you have some code that needs to make decisions based on when a user first opened their account. Now, to make this difficult, the code is separated by a couple intermediate objects from the user account object. The fragile approach would be to chain the calls back to the user account. For example:
if ( Owner.Owner.Account.StartDate < DecisionDate )
{
DoSomethingSpecial();
}
You can substitute code from your own language of choice if you like. So what’s so bad about this approach? The problem is we are making some assumptions that may not always hold true. Let’s say you got a Null Pointer/Reference exception in your if statement. Where do you look first? It could be that your object doesn’t have an owner. That wouldn’t be uncommon in a unit test environment. It could be your object’s owner doesn’t have an owner. Easy to miss in unit testing. Or it could be that there is no account. Perhaps it’s not the account but there is no date and the comparison is throwing the exception. That’s four things that can go wrong just on the left side of the comparison statement.
Degrees of Separation
So, we still need to make a decision on the user’s account start date. If we have corrupt data, or we are migrating accounts from one back end server to another, our code might break suddenly. If we were to be truly defensive, we would need a very complex null checking condition before we made this simple check. In the case we outlined above, we have three degrees of separation from the information we need. Really, what we need is direct access to the account.
You should have no more than one degree of separation from any information you need.
Something the component folks got right, is that by definition every component has direct access to the information it needs. The container, or controlling object takes care of that for us. The mechanisms to get at that information vary between the component systems, and can even be more complex than necessary.
If the Account object is central to your application, and multiple objects need to access it, you have a couple choices. You can make the Account static and centrally accessible. That’s great for easy access, and might even work if you can guarantee only one user’s account will ever be needed at one time. It’s a solution that would work for a desktop application with a single document interface. If you work on the web, or you want to let your users have more than one document open at a time, a centrally accessible static object will introduce other fragilities that are not worth the trouble.
Another solution would be to provide a lookup interface to find the right Account. This is the solution that some component frameworks use (ahem. JNDI, Avalon, etc.). A pro here is that you can do some cool things to make sure you get the right account. It allows you the flexibility of serving some accounts from LDAP while some are in a database. Problem is, we have left the world of POO and introduced a lot of complexity we probably just don’t need.
A better solution would be the judicious use of the Hollywood Principle. “Don’t call us, we’ll call you.” Some of the POJO (or POO) proponents will object, exclaiming that’s exactly the thing we wanted to avoid. Component frameworks like Spring, Castle, PicoContainer use this approach extensively. The only thing that the component frameworks do for you is to automate how the component objects are assigned to each other. We don’t need to go all the way down the path of using a component architecture. All we need to do is ensure that all children objects are assigned the same Account object that it has.
Now we have one degree of separation. We write the test to make sure children objects get assigned the account object that the parent has. We write the test to make sure the child object can use the account properly. This makes it easier to mock out the Account object and simplify our test setup.
In our tests, we should only ever have to assign the object(s) that will be used directly by the code we are testing. We should never have to create a whole hierarchy of objects for code to work. When something goes wrong with your object chain, the only way to find out exactly what it is will be to break out the debugger and step through your program. Any change to any object in the chain can potentially break the code you are writing. That is why we only want one degree of separation between any information we need and the object we are writing.
Say it with me, Singletons are Evil
I’m sure we can all point to a situation where having a singleton was very useful, and made code a lot easier. If we are honest with ourselves, we’ll realize the number of times it worked we can count on one hand, or maybe even one finger. The problem isn’t necessarily the singleton at the time it was created. The problem has more to do with how code evolves.
When you have an object that is easily accessible from anywhere in your program, more and more code will depend on that object being there. Then the code will depend on the objects that it can get from the singleton. Then you have code that accesses the singleton that would be better off written differently. Over time that singleton becomes a point of contention in your application.
In this day of multi-core processors and the demand for multi-threaded or multi-process applications, having a single object that all other objects access introduces subtle concurrency errors. Even if you handle the concurrency problems well with mutexes and a slick locking mechanism, you introduce bottlenecks and even the risk of deadlock because all your code is accessing this one singleton.
It’s worse if you mix the singleton with the multiple dimensions of separation problem we talked about earlier. Now, instead of proper object oriented design, you are developing a procedural application that mixes the worst from both object programming and procedural programming metaphors.
Singletons also are a sore point with testing. The problem is that if the singleton carries any state whatsoever, the effects of one test will affect other tests. If the tests are not executed in the correct order, they may not pass. There’s nothing more frustrating than trying to figure out why a test provides different results when it is doing the same thing. The code behaves insanely, when the tests expect it to behave the same way every time.
There’s More to Good Architecture
I don’t have the time or space to write out every little thing that can go wrong. The important approach is to simplify your system as much as possible. If you can compose your application of several well contained classes that behave predictably in tests, you stand a better chance of writing a solid application. Every version of your application you release, think to yourself, “Is there anything I can get rid of?” Or “Can I do the same work with less code?”
To me, an impressive mark of a solid application is how little code is needed to do all that it does. The more lines of code, the more opportunities for something to go wrong. The more moving parts, the more unpredictable the application becomes. Pursue simplicity, one step at a time.