Refactor to Delegation in preference to Abstract Classes

Minimize, eliminate, delegate, and routinize. Decide what's important and forget the rest. - Donna N. Douglass.

I've noticed a few times that factoring the difference between subclasses into explicit delegation results in cleaner code that is easier to test, especially when I use mock objects. Perhaps this is a useful Mock Object testing pattern: Replace Duplicated Behaviour with Delegation Through an Interface.

I recently refactored some of the jMock code to hook customisable formatting into the code that generated error messages on test failure. The end result is that InvocationMocker objects - objects that represent expectations or stubs - delegate to a Describer interface when asked to give their description and the concrete describer can be set when the object is constructed. In this way, the objects of the generic framework can be configured to create error messages that reflect the high level API used to compose them.

As part of the refactoring, I found a subclass of InvocationMocker that existed just to override the default describe implementation (to give no description, as it happens). I replaced instantiations of this class with instantiations of the base class with a custom Describer. This is a much cleaner design and much easier to test. The delegation of the descriptions to a Describer object is very easy to unit test with mock objects; the existing implementation and the overridden version were only tested in the acceptance tests and not actually unit tested at all.

If we had originally factored the differences between base class and subclass into a separate object, and defined the interaction with that object with an interface, the Describer design would have been already implemented by the time we needed it. We would also have been better able to test our classes. We should have listened to our tests and pulled out that interface when we originally wrote the subclass of InvocationMocker.

Update: Ivan Moore has posted a good article about refactoring inheritance into composition and delegation.

Copyright © 2004 Nat Pryce. Posted 2004-02-23. Share it.