ExtendingInterfaces

From APIDesign

Jump to: navigation, search

Adding new methods into already published interfaces is not backward compatible. Obviously it is not source compatible, as classes that extended the interface in one version, will no longer compile (due to missing implementation of the method). In Java it is sort of binary compatible, as already compiled classes (with missing implementation of new interface method) can be loaded and successfully linked into the JVM. Still such addition is almost a nightmare if the amount of callers into the interface is unrestricted - everyone calling new method needs to be ready to catch a runtime exception (thrown when calling the interface method on a class that does not provide its implementation).

Thus adding an abstract method into existing types is a big no-no in API design world. Much better is to split the API into ClientAPI and ProviderAPI. In spite of this, some, already public APIs contain interfaces that serve both roles. E.g. that can be called by unlimited number of clients as well as implemented by unlimited number of providers (not everyone is using ImplementOnlyInterface). What can be done when one needs to extend such interfaces with additional methods?

Nice and compatible way for ProviderAPI part of such interface is to define new interface, possibly extending the original one. The classes implementing the old interface can remain unchanged, the classes wishing to add support for new method may implement both interface. To help discoverability of such subclasses one can use Java's nested classes:

/** @since 1.0 */
public interface EditorCookie {
  public void JEditorPane[] getOpenedPanes();
 
  /** @since 2.0 */
  public interface Observable extends EditorCookie {
    public void addChangeListener(ChangeListener l);
    public void removeChangeListener(ChangeListener l);  
  }
}

However having such rich set of types is not really comfortable for clients of such API. Everytime they hold an instance of the version 1.0 interface, they need to use if (obj instanceof Observable) which leads to very verbose and hard to maintain code. Of course you do not want such code to be spread in the whole codebases of all your API users.

Define static utility class! Move the ugly instanceof code into it:

public final class Editors {
  private Editors() {}
 
  public static registerListener(EditorCookie ec, ChangeListener l) {
    if (ec instanceof EditorCookie.Observable) { 
      ((EditorCookie.Observable)ec).addChangeListener(l);
    } else {
      // implement the listening somehow or do nothing
    }
  }
}

Then you can instruct your API users to use this static utility method instead of directly dealing with the interface itself. This is what NetBeans recently did to optimize performance of finding most recently active editor.

Indeed this is not really the best pattern to start with, it is better to count with evolution and split ClientAPI and ProviderAPI as advocated in Chapter 8, but in case you managed to publish an interface with double purpose, this is relatively acceptable way to recover from such mistake.

Personal tools
buy