Design to implement filtering chain
design-patterns, java, oop, spring
Solution
For this case I would try to model the chain and move the responsibility of execution to the chain itself by giving a little bit more of "intelligence" to the nodes. In a way I would consider the nodes as being commands, in the sense that they can execute themselves and, by having a common interface, be able to create composites. (Btw, I'm not a Java programmer, so please forgive me for the possible syntax errors). So, my main design decisions would be:
- The chain knows how to execute itself.
- The chain nodes can be composites.
I would start by defining something like:
public abstract class ChainNode {
public abstract Content apply(Content content);
}
/**
The NullObject of the chain
http://www.oodesign.com/null-object-pattern.html
*/
public class FinalNode extends ChainNode {
public Content apply(Content content) {
return content;
}
}
/** A sample filter */
public class FilterA extends ChainNode {
private ChainNode nextNode;
FilterA(ChainNode nextNode) {
this.nextNode = nextNode;
}
public Content apply(Content content) {
filteredValue = //Apply the filter
return nextNode.apply(filteredValue);
}
}
/** An if-then-else filter */
public abstract class ConditionalFilter extends ChainNode {
private ChainNode trueFilter;
private ChainNode falseFilter;
ConditionalFilter(ChainNode trueFilter, ChainNode falseFilter) {
this.trueFilter = trueFilter;
this.falseFilter = falseFilter;
}
public Content apply(Content content) {
if (this.evalCondition(content)) {
return this.trueFilter.apply(content);
}
else {
return this.falseFilter.apply(content);
}
}
private abstract boolean evalCondition(Content content);
}
Whit this approach what you are doing is turning the control structures into objects and asking them to execute, which even allows you to create a logic different than the standard if-then or switch statements. With this base you could create a chain that has different branching operators, triggering different filtering paths.
Some things to note are:
- I assumed that a filter returns something of type `Content`, which actually allows you to chain one filter after the other. I guess that is true in your requirement, but I'm not sure.
- For each new filter you just create a new class and define the `apply` method.
- The null object is always the last node of a chain, stopping the chain calls.
- To define a new "branching node" you have to subclass from `ConditionalFilter` and redefine the `evalCondition` method. I don't know if Java has closures (I think it doesn't), but if it has you could instead add a `condition` instance variable and parametrize it with a closure, thus avoiding subclassing for each new condition. Or maybe there is a more accepted workaround for things like this in the Java world, I just don't know :(.
- I assumed that the conditional branching is decided based on the `content` parameter. If you need more information to make the decision you could have also a context object passed in the `apply` method. Depending on your needs this can be a structured object or just a dictionary if you need more flexibility.
Finally, regarding the construction of the chains, if the chains are long and complex to build I think a builder here should suit your needs.
HTH
Problem
I have to design entities like Filters, represented by `Filter` interface, declaring the `apply(Content content)` method, which can be applied to a Content object. Filters can be composed together in chains, similar to workflows, but these are dynamic. For example, if FilterA return X, then I will apply filterB, while receiving result Y will cause to apply FilterC. The chain of the filters is application-specific, and I haven't yet decided how to allow the construction of filter chains. I would design this behavior in the same way of some workflow frameworks: a manager component iterating over a list of filters, and calling `filter.apply(content)` on each filter. But how to allow dynamism like if/else statements? For now I conceived a Workflow or FilterChain interface, declaring a `getNextFilter(previousResult)`. Implementing this interface one can declare an application-specific workflow. But the implementation of Workflow interface will be boring: to keep track of current step (an integer?) and then at each `getNextFilter()` invocation, determining which filter will be the next, through a switch/case statement?!? Which solution could be better? How to declare a chain? I'm using Java and Spring, so IoC can be used.