Refactoring Interfaces

Many types of DC power plug

In our 2004 OOPSLA paper, Mock Roles not Objects, Steve, Joe, Tim and I described how we used Mock Objects and TDD to guide the design of object-oriented software. Briefly, we described the process as:

Unfortunately we neglected to describe a vital part of the process: refactoring. As the system grows we look out for interfaces that define similar ways in which objects collaborate - common patterns of communication between objects. We then collapse different interfaces that are incompatible but semantically equivalent into the same type.

We took for granted that when programmers refactor a system they apply as much refactoring effort to the interfaces between the objects as they do to the classes of the objects themselves. However, I have found that this is not the case. For example, Martin Fowler's canonical book of Refactoring patterns does not contain any patterns about refactoring interfaces or the communication protocols between objects.

If you follow the interface discovery process without refactoring the interfaces you discover, you end up with a system containing lots of interfaces, many of which represent very similar concepts in incompatible ways. Objects that should be plug-compatible cannot communicate without lots of awkward little adapter classes. As a result, I have found that teams develop a negative reaction to interfaces or even object-oriented design and end up with a design that is difficult to change because its classes are statically coupled.

When you refactor interfaces into a set of common communication patterns, objects in the system become much more "pluggable". You can then change the behaviour of the system by changing the composition of its objects-adding and removing instances, and plugging different combinations together-rather than writing procedural code. The code that composes objects acts a declarative definition of the how entire system will behave. You therefore end up working at a much greater level of abstraction and can focus on what you want the system to do, and not how that is implemented.

Obviously you need to strike a balance. If you end up with interfaces like the following, you've gone too far!

public interface Thing {
    Object doSomething(Object arg) throws Exception;
}

Two refactoring steps I often apply are:

Copyright © 2008 Nat Pryce. Posted 2008-10-03. Share it.