Object-Oriented Design for Applications with Pluggable Components – Java Guide

Architecturedesigndesign-patternsjavaobject-oriented

I'm trying to refactor an ugly code and make it easly extendable in the future.

The application should be nothing else but a series of components that have input(s) and output(s). The components are chained in such manner that the current component's input is the output of one (or more) previous components.

Here's a quick overview of what I have so far:

  1. Reader

    • Denotes a data source
    • Can be a file on HDD, online resource, database, etc.
  2. Splitter

    • Input is Reader(s)
    • Splits the contents of what reader delivers into parts
    • Outputs are split contents of Reader(s)
  3. Model

    • Input is Splitter(s)
    • Creates a model of something based on Splitter(s) outputs
    • Output is silent, but you can say that the output is an internal state that can be queried for individual inputs
  4. Tester

    • Input is a model
    • It can query a model for a result on some data
    • Output is optional, but in case it is used, it's a stream of (queryInput, queryOutput) data
  5. Writer

    • Input is practiacally anything that produces a collection of objects
    • Writes that collection to wherever
    • I dunno what the output should be right now

So, I want to do have the ability to plug them in the following manner:

-->Reader-->Splitter-->Model-->Tester-->Writer-->

However, this would also be a valid combination (it obviously does nothing more but a simply data transfer)

-->Reader-->Writer-->

Now, since I'd like to be able to plug (almost) everything to everything and (possibly) create quite long chains, I'd assume I'd have to have some sort of Pluggable interface.

Also, when creating such big chain, I'd probably want to wrap it behind a Facade. And since I'd want each pluggable component (class) to be replaced by some other, then Strategy pattern comes to mind.

Now, since I've already mentioned the term chain here, Chain of Responsibility pattern comes to my mind, in the following (or similar way):

public interface Pluggable<InputType, OutputType> {
    public void consume(Pluggable<?, InputType> previous);
    public Collection<OutputType> produce();
}

For example, if I wanted to have my Splitter to split a list of Files provided by Reader, it might looks something like this:

public class Splitter<InputType, OutputType> implements Pluggable<?, InputType> {
    public void consume(Pluggable<?, InputType> previous) {
        // retrieves for example a list of InputType objects
    }

    public Collection<OutputType> produce() {
        // filters the collection it got and returns a sublist for example
    }
}

In the end, the components might look something like this:

Reader<File, Foo> --> Splitter<Foo, Foo> --> Model<Foo, Void> --> Test<Model, Bar> --> Writer<Bar, DAO>

I don't know how else to describe the problem I'm having, but I'm sure something like this is quite achieveable. For visual example, here's the image of RapidMiner's process

RapidMinerChainExample

Note that I'm not trying to replicate or copy Rapid Miner, it's just that the project I was assigned looks like it can be implemented in simiar way.

I'd appreciate any help regarding how to design such application.

Best Answer

No, Chain of Responsibility doesn't make sense here, because it assumes all components have same interface. I don't think Java's type system is good enough to make this fully generic, so I would opt in to type erasure and some kind of "manager" that pipes output of one module into input of next one, while encapsulating the erasure.

The module's interface would look something like this:

public interface Pluggable {
    public Class inputType();
    public Class outputType();
    public Object handle(Object previous);
}

The manager would accept list of instances that implement this interface, check if their input/output classes work and then do the piping between them.

If this was C#, I would make it even nicer by creating abstract class, that would be generic in same way as your interface, I would implement the Pluggable explicitly(to hide it from sight) and implement the inputType and outputType with typeof() and create abstract and wrap the handle method in generic abstract method. I don't know how that would be possible in Java.

public interface IPluggable
{
    Type InputType { get; }
    Type OutputType { get; }

    Object Handle(Object value);
}

public abstract class Pluggable<TInput, TOutput> : IPluggable
{
    Type IPluggable.InputType { get { return typeof(TInput); } }
    Type IPluggable.OutputType { get { return typeof(TOutput); } }
    object IPluggable.Handle(object value)
    {
        return Handle((TInput)value);
    }

    protected abstract TOutput Handle(TInput value);
}

This is how the "manager" class might look like. Sorry C# again.

public class PluginPipeline
{
    public static PluginPipeline Create(IEnumerable<IPluggable> plugins)
    {
        EnsurePluginsAreInRightOrder(plugins);
        return new PluginPipeline(plugins);
    }

    private static void EnsurePluginsAreInRightOrder(IEnumerable<IPluggable> plugins)
    {
        Type previousType = typeof(object); // first is little special..
        foreach(IPluggable plugin in plugins)
        {
            if (plugin.InputType != previousType)
                throw new Exception("Invalid link in pipeline!");

            previousType = plugin.OutputType;
        }
    }

    private readonly IEnumerable<IPluggable> _plugins;

    private PluginPipeline(IEnumerable<IPluggable> plugins)
    {
        _plugins = plugins;
    }

    public void Execute()
    {
        object previousValue = null;

        foreach(IPluggable plugin in _plugins)
        {
            previousValue = plugin.Handle(previousValue);
        }
    }
}
Related Topic