The Trouble with Interfaces
Developer's Cave July 9th. 2008, 11:46amJava interfaces are a wonderful thing; don’t get me wrong. However, many interface contracts make assumptions about how the implementation will behave. What is more, Java offers no way to programmatically enforce such an interface contract.
This article discusses a particular example of interface behavioral assumption and how to (partially) enforce the behavior programmatically.
In this case, consider an interface method that specifies a return value, such as:
public interface Namable { /** * Get the name of this object */ public String getName(); }
Implementing this interface is deceptively simple – just return whatever “name” is appropriate. However, the contract is missing several behavioral rules:
- Is
nulla valid return type? - When is this method queried?
- Is the name requested once or multiple times?
- Can the name change during the lifetime of the implementation?
In many cases, the assumption is that the method will always return the same value. Java interfaces offer no programmatic way to directly enforce such a behavioral contract. To improve code reliability, I’ve often resulted to creating a so-called default implementation that enforces the contract:
public final class DefaultNamable implements Namable { private final String mName; public DefaultNamable(String name) { mName = name; } public final String getName() { return mName; } }
Finally, actual implementation classes can implement the interface, dispatching to the contained default implementation:
public final class SomeNamableEntity implements Namable { private final Namable mNamable; public SomeNamableEntity(String name) { mNamable = new DefaultNamable(name); } public String getName() { return mNamable.getName(); } }
Of course, this Namable example is too simplistic to really need a DefaultNamable implementation. This pattern can assist with more complex interface implementations, which require more internal data structure bookkeeping and/or have more subtle contract requirements.
As an alternative, instead of the final DefaultNamable class, create an abstract AbstractNamable implementation (or allow DefaultNamable to be extended). Either way, SomeNamableEntity could then extend the helper implementation, avoiding the duplicate method declarations required for containment and dispatch. This works only if SomeNamableEntity does not need to extend some other class.
If anyone finds this sort of pragmatic programming practice post interesting, please leave a comment. I can ramble on like this for months…













July 9th, 2008 at 2:32 pm
You have described the Delegation Pattern.
The multiple inheritance of Python alleviates a lot of that tedium, but other features of Python that relax restrictions kind of make “contracts” impossible.
July 9th, 2008 at 2:47 pm
Yes, thank you. This is a perfect example of the delegation pattern at work.
Python seems to be an attempt at a less-strict Java. As you may have gleaned, I’d like to see a more-strict Java.