OpenClass

From APIDesign

Revision as of 16:26, 25 December 2008 by JaroslavTulach (Talk | contribs)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search

When dealing with objects that produce and consume generic type of unknown bounds, it is necessary to open the type before its use. One can either use an OpenMethod or, in case the open needs to last longer than just during method execution, an OpenClass.

Imagine a generified API to access project configuration:

Code from ProjectConfigurationCorrect.java:
See the whole file.

interface ProjectConfigurationProvider
    <Configuration extends ProjectConfiguration> {
    public Configuration[] getConfigurations();
    public Configuration getActive();
    public void setActive(Configuration c);
}
interface ProjectConfiguration {
    public String getDisplayName();
}
 

The getActive' and setActive methods could take ProjectConfiguration class as parameter, but that would not be correct - it would allow a user of the API to take configuration from one project and try to activate it on different one. By use of <Configuration extends ProjectConfiguration> bound, this is prevented. Yet, this very complicates life of clients of this API, as following code:

Code from ProjectConfigurationOriginal.java:
See the whole file.

interface ProjectConfigurationProvider {
    public ProjectConfiguration[] getConfigurations();
    public ProjectConfiguration getActive();
    public void setActive(ProjectConfiguration c);
}
interface ProjectConfiguration {
    public String getDisplayName();
}
 

no longer compiles. Instead of it one needs to either use the OpenMethod:

Code from ProjectConfigurationCorrect.java:
See the whole file.

private static <C extends ProjectConfiguration> void resetToZero(
    ProjectConfigurationProvider<C> provider
) {
    provider.setActive(provider.getConfigurations()[0]);
}
 

or OpenClass:

Code from ProjectConfigurationCorrect.java:
See the whole file.

static void workWithProjectConfigurationProvider(
    ProjectConfigurationProvider<?> p
) {
    ResetToZero<?> rtz = ResetToZero.create(p);
    rtz.obtainFirst();
    // after a while
    rtz.apply();
}
 
static class ResetToZero<C extends ProjectConfiguration> {
    C active;
    final ProjectConfigurationProvider<C> provider;
 
    ResetToZero(ProjectConfigurationProvider<C> provider) {
        this.provider = provider;
    }
 
    static <C extends ProjectConfiguration> ResetToZero<C> create(
        ProjectConfigurationProvider<C> p
    ) {
        return new ResetToZero<C>(p);
    }
 
    public void obtainFirst() {
        active = provider.getConfigurations()[0];
    }
 
    public void apply() {
        provider.setActive(active);
    }
}
 

This is very annoying and definitely not clueless! As such exposing patterns that require OpenClass or OpenMethod to ClientAPI users is not really recommended.

On the other hand, as Singletonizer pattern shows, using this style in ProviderAPI, where the open is done once by the API infrastructure is quite OK.

Personal tools
buy