Tuesday, May 25, 2010

Half baked objects

A half baked object is an object that after construction is still not ready to use.
Using it will either just silently behave badly (that's bad) or throw a usage exception (that's less bad).

This is a really irritating code smell, because it makes it much too easy to introduce bugs. The best APIs I've used have made it impossible (or atleast very difficult) to use it incorrectly because once they've given me an object to work with, they've guaranteed that they're set up properly and are ready for action.

There are some generic rules you can use to avoid having half baked objects:
  1. Set up everything in the constructor. This is not always possible, but when it is, do it.
  2. Use final fields - they enforce you set up everything in the constructor, which reduces the risk for bugs. Note that final fields in itself doesn't make the object immutable, you can still have final lists, maps and sets in your object, which themselves are mutable.
  3. Use builders - and don't allow creating the real object until the builder has all the necessary requirements.
  4. Be suspicious of methods named things like "init", "postconstruct", "setup" or similar. They could be indicators of a half baked object.
However, I have found one case of needing half baked objects, and I currently don't have any workaround for it.
This is a new feature in Mockachino, where I want to create an alias for a combination of a mock object and a method signature.
Since I can't pass a method signature as a parameter in Java (without using icky reflection) my only choice is to capture the method invocation and record it in the Alias.


interface Foo {
foo(int n);
}


Foo mock = mock(Foo.class);

mock.foo(123);

SimpleAlias alias = newAlias(); // alias is half baked here - it's not bound to any mock

alias.bind(mock).foo(anyInt()); // now alias is fully setup

alias.verifyOnce();


This is one of few cases where half baked objects is hard to avoid, but if anyone has any clever suggestions, I'm all ears.

3 comments:

Unknown said...

what about

SimpleAlias = aliasFor(mock).foo(anyInt());

where aliasFor is static factory method of SimpleAlias that calls private constructor

Kristofer said...

Doesn't work, since the return type of foo isn't SimpleAlias. In fact, the return type may even be void.

Unknown said...

Sory, I didn't think it through. However I came up with an idea to use something like history of mock instead of alias.

Foo mock = mock(Foo.class);
mock.foo(123);
mock.bar(456);

Foo fooHistory = historyOf(mock);
history.foo(anyInt());
history.bar(anyInt());

verify(history);

Just some food for thought. I wonder what are other use cases for alias other than verifyOnce()

Post a Comment