Decouple abstract class details from extended implementations

I have a PluginClassLoader which is an abstract class that provides 99% of the functionality for my project's class loaders. I have 2 subclasses (ServiceClassLoader, and ChannelClassLoader) which extend PluginClassLoader and are little more than wrappers plus some customized logging. 99% of the logic is the same between the two implementations.

I then have a PluginManager which is also an abstract class which has 2 implementations that extend it (ServiceManager and ChannelManager) which are wrappers plus customized logging and a more convenient constructor.

The trouble I'm having is, in my PluginManager is must be able to instantiate a new class loader type, of either ServiceClassLoader or ChannelClassLoader. I'm trying to avoid having my PluginManager coupled to it's current implementations (ie. I want the flexibility of being able to add future implementations but not change PluginManager's logic), so trying to avoid passing in some Enum and using some:

if (classLoaderType instanceof ClassLoaderType.SERVICE) {
    // do logic for instantiating ServiceClassLoader
}

Sample class hierarchy:

public abstract class PluginManager {
    // logic for managing plugins and when to load them
    // ...
    // somewhere deep in a loadPlugin(final File directory) method
    pluginLoader = new PluginClassLoader(); // <-- not valid, can't instantiate 
                                            //         an abstract class, 
                                            //         and it's of the wrong type!
}

public abstract class PluginClassLoader extends URLClassLoader {
    // class loader logic
}

public class ServiceManager extends PluginManager {
    // wrapper for PluginManager with some customized logging
}

public class ServiceClassLoader extends PluginClassLoader {
    // wrapper for PluginClassLoader with some customized logging
}

Trying to avoid doing something like:

public abstract class PluginManager {

    private final PluginType pluginType;

    public PluginManager(final PluginType pluginType) {
        this.pluginType = pluginType;
    }

    // logic ...

    // somewhere deep in the loadPlugin(final File directory) method
    if (pluginType instanceof PluginType.SERVICE) {
        pluginLoader = new ServiceClassLoader();
        // more logic
    } else if (plugintype instanceof PluginType.CHANNEL) {
        pluginLoader = new ChannelClassLoader();
        // more logic
    }
}
Jon Skeet
people
quotationmark

Three options:

  • Declare an abstract newClassLoader() method in PluginManager, which is overridden in ServiceManager to return a new ServiceClassLoader etc
  • Change your parameter type from PluginType to Class<? extends ClassLoader>, store it in a field (e.g. classLoaderClass), and then just call classLoader.newInstance() when you need to
  • Make PluginType an enum (if it's not already) which have its own method to create a new ClassLoader.

(It's not clear whether you need PluginType for other reasons - if you don't, then don't have it.)

people

See more on this question at Stackoverflow