New page: Adding new methods into already published interfaces is not backward compatible. Obviously it is not source compatible, as classes that ex...

New page

Adding new methods into already published interfaces is not [[BackwardCompatibility|backward compatible]]. Obviously it is not [[SourceCompatibility|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 [[BinaryCompatibility|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 [[API]]s contain interfaces that serve both roles. E.g. that can be called by unlimited number of [[ClientAPI|clients]] as well as implemented by unlimited number of [[ProviderAPI|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:

<source lang="java">
/** @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);
}
}
</source>

However having such rich set of types is not really comfortable for [[ClientAPI|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:
<source lang="java">
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
}
}
}
</source>

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 [http://hg.netbeans.org/ergonomics/diff/218c176d8a97/openide.text/src/org/openide/text/NbDocument.java did] to optimize [[performance]] of finding most recently active editor.

Indeed this is not really the best [[APIDesignPatterns|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.

[[Category:APIDesignPatterns]] [[Category:APIDesignPatterns:Evolution]]
[[Category:APIDesignPatterns:Anti]]