'. '

ImplementOnlyInterface

From APIDesign

Jump to: navigation, search

By exposing a Java interface in an API one usually creates two sides of an API. One for clients that may call methods on instances assignable to that interface and one for providers that may implement that interface. This is often not really desirable. Each of these API types has different evolution characteristics. Also the clarity of such interface methods may not be as clear as it could. More about this in ClarityOfAccessModifiers and ClarityOfTypes.

Most common example of such interface with double purpose is java.lang.Runnable. Everyone implements it, there is a lot of methods in Java standard libraries that also accept it. Another group of such interfaces includes almost all JavaBeans listeners. Implementing java.beans.PropertyChangeListener is example ProviderAPI. Almost everyone wrote such code at least once. Also, as JavaBeans specification is common, a lot of programmers also used the interface in client way after adding following code into their classes:

public void addPropertyChangeListener(PropertyChangeListener l) { this.listener = l; }
public void removePropertyChangeListener(PropertyChangeListener l) { this.listener = null; }
protected final void fireChange() {
  this.listener.firePropertyChange(new PropertyChangeEvent(this, null, null, null));
}

Interfaces contain public methods which are known to have multiple meanings as such they are often subject to double use. One can prevent that by usage of ImplementOnlyAbstractClass pattern, yet it may not be always the preferred solution.

Contents

Implement Only Interface

On the other hand, the algorithm to redesign multi meaning classes into types with clear meanings suggests to use create interface Provider and claims that such interface methods have just a single meaning. Can interface have just a single meaning? parren questioned that:

The Provider interface is a public interface residing
within a public class. I can implement this interface and throw it 
around at will. For that reason, instances of the interface are 
accessible to clients of the API. For the same reason, they say to me:
implement me and do whatever you want with me, including call me.

Sort of true, but I still insist that the interface methods have only single meaning within its API. Imagine there is thousands users of following API:

Code from NonMixedFactory.java:
See the whole file.

public final class NonMixedFactory {
    private NonMixedFactory() {
    }
 
    public static NonMixed create(Provider impl) {
        NonMixed api = new NonMixed(impl);
        Callback callback = new Callback(api);
        impl.initialize(callback);
        return api;
    }
 
    public interface Provider {
        public void initialize(Callback c);
        public int toBeImplementedBySubclass();
    }
 
    public static final class Callback {
        final NonMixed api;
 
        Callback(NonMixed api) {
            this.api = api;
        }
        public final void toBeCalledBySubclass() {
            api.counter++;
        }
    }
}
 

These usages are packaged in their own modules (independent compilation units) and they do not know about each other. They only compile against and use the above API. Each of them can create a class that implements the Provider interface. However the only useful thing that can be done with such class, is to create its instance and pass it into the NonMixedFactory.create method. But that method keeps the instance for private purposes and does not let any other user of the API to access it.

There is no way some other module using API could get access to instance of such Provider implementation. As such the Provider methods has just a single meaning: implement me!.

Self Usage

One might argue, that the same module (e.g. compilation and runtime unit) can start to access its own implementation:

class MyProvider implements Provider { ... }
 
Provider p = new MyProvider();
p.toBeImplementedBySubclass();

True, this is possible use. More misuse than a use however. One could call the MyProvider methods directly by declaring the variable to be of MyProvider type. So the usage of Provider in this example is unnecessary.

However even with Provider variable, the meaning of Provider in the NonMixedFactory API is still clear. That fact that one module decides to internally misuse it can't affect that.

Reexporting API

Possible problem may arise when the MyProvider class is part of API of its module. Then other modules can use it and indeed, can also call its methods. Then the clarity of the Provider methods is lost. However this is not fault of the original NonMixedFactory API, but fault of this new one which re-exports the interface.

The original API has still its clarity, but the whole system's single meaning is compromised by additional APIs. There are just tho things the author of the original API can do about this:

I personally prefer to just hope. So far it work fine, I have not found a module that would improperly re-export my API interfaces.

By Passing

Last way for the Provider implementation to get into hands of other modules is escaping through some other API. For example one could:

class MyPanel extends JPanel implements NonMixedFactory.Provider {
  ...
}

Then other modules can get access to such JPanel through AWT component hierarchy and use instanceof check and cast to safely get foreign Provider implementation which can then be called.

However the fact that there is some JPanel that can be cast to Provider is an example of another API type - a kind of runtime API. This means the module providing MyPanel is also providing additional API (an information that there can be a JPanel that also implements Provider) and we are in situation similar to one analysed in Reexporting API section.

Personal tools
buy