user1781028
user1781028

Reputation: 1508

Design to implement filtering chain

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.

Upvotes: 5

Views: 10814

Answers (5)

Andrés Fortier
Andrés Fortier

Reputation: 1693

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:

  1. The chain knows how to execute itself.
  2. 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:

  1. 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.
  2. For each new filter you just create a new class and define the apply method.
  3. The null object is always the last node of a chain, stopping the chain calls.
  4. 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 :(.
  5. 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

Upvotes: 3

StalkAlex
StalkAlex

Reputation: 743

Here's example of composite pattern implementing filter chain in php.

$filter = new BookFilter\Chain();
$filter->appendFilter(new BookFilter\Isbn('978-5-8459-1597-9'))
       ->appendFilter(new BookFilter\Title('Domain Driven', BookFilter\Title::CONTAINS))
       ->appendFilter(new BookFilter\Publisher('Вильямс', BookFilter\Publisher::EQUALS))
       ->appendFilter(new BookFilter\Price(100, 10.000))
       ->appendFilter(new BookFilter\Ebook(true));
$bookCollection = $bookSeachService->findAllByFilter($filter);

Taken from here: http://crazycode.net/blog/6-architecture/10-structural-patterns

Upvotes: 1

Cwt
Cwt

Reputation: 8576

For generality I am assuming, that the conditional should decide not only between single filters but between filter chains.

After some thought it seems to me that the Composite Pattern fits quite nicely here.

  • Component: your Filter interface
  • Leafs: concrete filters
  • Composite: your "Workflow or FilterChain interface"

The ConditionalFilter could be either a Leaf or a Composite. In both cases, initialized with a compare and two Filter objects, it can branch on either single filters or filter workflows.

Upvotes: 2

Rob
Rob

Reputation: 11733

What you need here is the pattern Chain of Responsibility.

Having implemented this many times in Java, my recommendations would be:

  • you probably don't need a complete chain somewhere: each item will have a link to its successor
  • if you do need a complete list of members in the chain, you can probably use a LinkedList.
  • remember you can have multiple chains
  • key design issue is whether there is any partial handling, generally there is not: each member of the chain looks and if it can handle it does, otherwise, it invokes its successor

The participation mechanism should be an interface, that way you could have specialized agents that do very different things and agree only to receive messages and pass them on according to the interface dictate.

If you want to progressively filter, you can pass the content along, as you show in your question, and each participant can mutate it. I have an open source project on GitHub called scraper that uses Chain of Responsibility to implement chains of agents that extract the parts of the page being scraped that would contain content.

Upvotes: 0

Michael
Michael

Reputation: 2500

Since you are just wanting to execute the filter chain in order perhaps it would be simplest to implement your filter chain as a List<Filter>. You could just do a loop over them and execute. Something like (note this is obviously a quick implementation I've not tried):

public class FilterChainImpl implements FilterChain {
  private List<Filter> filterChain = new ArrayList<Filter>();

  public addFilter(final Filter f) {
    filterChain.add(f);
  }

  public apply(final Content content) {
    Content prevContent = content;
    for(Filter f : filterChain) {
      prevContent = f.apply(prevContent);
    }
  }
}

Then you can use this to generically create any filter chain you like. You might use some factory technique for creating the filter chains if you are going to have many different filter chains.

Upvotes: 0

Related Questions