Design for filtering chain implementation

I need to create objects like filters represented by an interface Filter

by declaring a method apply(Content content)

that can be applied to the Content object. Filters can be chained similar to workflows, but they are dynamic. For example, if FilterA returns X, then I will apply filterB, while returning the result Y will apply FilterC. The filter chain is application specific and I have not yet figured out how to enable the creation of filter chains.

I would design this behavior in the same way as some workflow frameworks: the manager component iterates over the list of filters and calls filter.apply(content)

for each filter. But how to resolve dynamism as if / else expressions?

I am currently conceiving a Workflow or FilterChain interface by declaring getNextFilter(previousResult)

. By implementing this interface, you can declare an application-specific workflow. But the implementation of the Workflow interface will be boring: keeping track of the current step (integer?) And then on each call getNextFilter()

, determining which filter will be next through a switch / case statement?!?

What is the best solution? How do I declare a chain?

I am using Java and Spring, so IoC can be used.

+3


source to share


5 answers


In this case, I'll try to simulate the chain and move the execution responsibility to the chain, adding a little bit of "intelligence" to the nodes. In a sense, I would consider nodes as commands, in the sense that they can execute themselves and, having a common interface, be able to create composites . (Btw, I'm not a Java programmer, so please forgive me for possible syntax errors). So my main design decisions:

  • The chain knows how to execute itself.
  • Chain knots 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);
}

      

In contrast to this approach, what you do is turn control structures into objects and ask them to execute, which even allows you to create logic different from the standard if-then or switch statements. With this base, you can create a chain that has different branching statements by running different filtering paths.



Some notes:

  • I assumed that the filter returns something of a type Content

    that effectively allows you to hook one filter after another. I assume this is true in your requirement, but I am not sure.
  • For each new filter, you simply create a new class and define a method apply

    .
  • The null object is always the last node in the chain, stopping chain calls.
  • To define a new "branch node", you must subclass from ConditionalFilter

    and override the method evalCondition

    . I don't know if Java has a closure (I think it doesn't), but if you could instead add an instance variable condition

    and parameterize it with a closure, thereby avoiding subclassing for every new condition. Or maybe there is a more acceptable solution in the Java world for such things, I just don't know: (.
  • I assumed that conditional branching is defined based on a parameter Content

    . If you need more information to make a decision, you can also have a context object passed in the method apply

    . Depending on your needs, it can be a structured object or just a dictionary if you need more flexibility.

Finally, as far as building the chains, if the chains are long and difficult to build, I think the builder here should match your needs.

NTN

+2


source


For generality, I am assuming that the condition should decide not only between individual filters, but also between filter chains.

After some thought it seems to me that the Composite Pattern fits very well here.



  • Component

    : your Filter

    interface
  • Leafs

    : concrete filters
  • Composite

    : your "Workflow or FilterChain interface"

ConditionalFilter

can be either Leaf

or Composite

. In both cases, initialized with a comparison and two objects Filter

, it can branch into one of the filters or filter streams.

+2


source


Here's an example of a composite schema implementing a 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

+1


source


Since you just want to execute the filter chain, so it might be easier to implement the filter chain as List<Filter>

. You could just loop over them and execute. Something like (note that this is obviously a fast implementation that I haven't 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 that to create a whole filter chain that you like. You can use some factory method to create filter chains if you have many different filter chains.

0


source


Here you will need the Chain of Responsibility template .

Having implemented this many times in Java, my recommendations are:

  • you probably don't need the whole chain somewhere: each element will have a reference to its successor
  • If you want a complete list of members in a chain, you can probably use LinkedList.
  • remember that you can have multiple chains.
  • The problem with key design is whether there is any partial processing, usually not: each member of the chain looks like, and if it can handle it, otherwise it calls its successor

The participation mechanism must be an interface, so you can have specialized agents that do different things and only agree to receive messages and transmit them as required by the dictate of the interface.

If you want to filter gradually, you can stream content together, as you see in your question, and each member can mutate it. I have an open source project on GitHub called scraper that uses Chain of Responsibility to implement chains of agents that fetch the parts of the page that are scrapped which will contain content.

0


source







All Articles