设计实现过滤链

时间:2013-01-27 22:35:56

标签: java spring oop design-patterns

我必须设计像Filter接口表示的过滤器这样的实体,声明可以应用于Content对象的apply(Content content)方法。过滤器可以组合在一起,类似于工作流程,但这些是动态的。例如,如果FilterA返回X,那么我将应用filterB,而接收结果Y将导致应用FilterC。过滤器链是特定于应用程序的,我还没有决定如何构建过滤器链。

我会以与某些工作流框架相同的方式设计此行为:管理器组件迭代过滤器列表,并在每个过滤器上调用filter.apply(content)。但是如何允许像if / else语句这样的动态?

现在我构思了一个Workflow或FilterChain接口,声明了getNextFilter(previousResult)。实现此接口可以声明特定于应用程序的工作流。但是Workflow接口的实现将很无聊:要跟踪当前步骤(整数?)然后在每次getNextFilter()调用时,通过switch / case语句确定哪个过滤器将成为下一个?!?< / p>

哪种解决方案可能更好?如何申报连锁?

我正在使用Java和Spring,因此可以使用IoC。

5 个答案:

答案 0 :(得分:2)

对于这种情况,我会尝试对链进行建模,并通过提供更多的&#34;智能&#34;来将执行的责任转移到链本身。到节点。在某种程度上,我会将节点视为命令,因为它们可以自己执行,并且通过具有公共接口,可以创建composites。 (顺便说一下,我不是Java程序员,所以请原谅我可能的语法错误)。所以,我的主要设计决定是:

  1. 连锁店知道如何自行执行。
  2. 链节点可以是复合材料。
  3. 我首先要定义类似的东西:

    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);
    }
    

    使用此方法,您正在做的是将控制结构转换为对象并要求它们执行,这甚至允许您创建与标准if-then或switch语句不同的逻辑。有了这个基础,你可以创建一个具有不同分支操作符的链,触发不同的过滤路径。

    需要注意的一些事项是:

    1. 我假设过滤器返回类型为Content的内容,实际上允许您将一个过滤器链接到另一个过滤器。我想你的要求确实如此,但我不确定。
    2. 对于每个新过滤器,您只需创建一个新类并定义apply方法。
    3. null对象始终是链的最后一个节点,停止链调用。
    4. 定义一个新的&#34;分支节点&#34;您必须从ConditionalFilter继承并重新定义evalCondition方法。我不知道Java是否有闭包(我认为它没有),但如果它有,你可以改为添加一个condition实例变量并用闭包进行参数化,从而避免每个变量的子类化新条件。或者也许在Java世界中有一个更为公认的解决方法,我只是不知道:(。
    5. 我假设条件分支是基于content参数决定的。如果您需要更多信息来做出决定,您还可以在apply方法中传递上下文对象。根据您的需要,如果您需要更多的灵活性,这可以是结构化对象,也可以只是字典。
    6. 最后,关于链条的构造,如果链条长而复杂,我认为这里的builder应该适合您的需要。

      HTH

答案 1 :(得分:2)

为了一般性我假设,条件不仅应该在单个过滤器之间决定,还应该在过滤器链之间决定。

经过一番思考后,在我看来Composite Pattern非常适合这里。

  • Component:您的Filter界面
  • Leafs:具体过滤器
  • Composite:您的“工作流程或FilterChain界面”

ConditionalFilter可以是LeafComposite。 在这两种情况下,使用比较和两个Filter对象初始化,它可以分支在单个过滤器或过滤器工作流程上。

答案 2 :(得分:1)

这是在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);

从这里采取:http://crazycode.net/blog/6-architecture/10-structural-patterns

答案 3 :(得分:0)

由于您只是想按顺序执行过滤器链,因此最简单的方法是将过滤器链实现为List<Filter>。你可以对它们进行循环并执行。 类似的东西(注意这显然是我没试过的快速实现):

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);
    }
  }
}

然后您可以使用它来一般性地创建您喜欢的任何过滤器链。如果要使用许多不同的过滤器链,可以使用一些工厂技术来创建过滤器链。

答案 4 :(得分:0)

这里需要的是模式Chain of Responsibility

在Java中实现了这么多次,我的建议是:

  • 您可能不需要在某个地方使用完整的链:每个项目都有一个指向其后继者的链接
  • 如果您确实需要链中的完整成员列表,则可以使用LinkedList。
  • 记住你可以拥有多个连锁店
  • 关键设计问题是是否有任何部分处理,通常没有:链的每个成员看起来如果它可以处理它,否则,它调用它的继承者

参与机制应该是一个接口,这样你就可以拥有专门的代理,这些代理做了很多不同的事情,只同意接收消息并根据接口指令传递它们。

如果您想逐步过滤,可以按照问题中的显示传递内容,每个参与者都可以对其进行变更。我在GitHub上有一个名为scraper的开源项目,该项目使用责任链来实现代理链,这些代理链提取被包含内容的页面部分。