【问题标题】:Design to implement filtering chain设计实现过滤链
【发布时间】:2013-01-27 22:35:56
【问题描述】:

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

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

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

哪种解决方案可能更好?如何声明一个链?

我用的是Java和Spring,所以可以用IoC。

【问题讨论】:

    标签: java spring oop design-patterns


    【解决方案1】:

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

    1. 链知道如何执行自己。
    2. 链节点可以是复合的。

    我将首先定义如下内容:

    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. 要定义一个新的“分支节点”,您必须从ConditionalFilter 继承并重新定义evalCondition 方法。我不知道 Java 是否有闭包(我认为没有),但如果有,您可以改为添加一个 condition 实例变量并使用闭包对其进行参数化,从而避免为每个新条件创建子类。或者,在 Java 世界中可能有更可接受的解决方法,我只是不知道 :(。
    5. 我假设条件分支是根据content 参数决定的。如果您需要更多信息来做出决定,您还可以在apply 方法中传递一个上下文对象。根据您的需要,这可以是结构化对象,如果您需要更大的灵活性,也可以只是字典。

    最后,关于链的构建,如果链长且构建复杂,我认为这里的builder 应该适合您的需求。

    HTH

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          【解决方案4】:

          由于您只是想按顺序执行过滤器链,因此将过滤器链实现为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);
              }
            }
          }
          

          然后您可以使用它来创建您喜欢的任何过滤器链。如果您将拥有许多不同的过滤器链,您可能会使用一些工厂技术来创建过滤器链。

          【讨论】:

          • 此解决方案不符合动态要求。如果条件 X 为真则应用 FilterA 或条件 X 为假时应用 FilterB 怎么办? FilterChain 不能是简单的for 语句。也许创建一个 Condition 对象来组成过滤器链?
          • 啊,我明白了,我误解了你的问题。我的解决方案绝对不适合你想要的。我不确定有什么简单/干净的方法可以做到这一点。
          【解决方案5】:

          这里需要的是Chain of Responsibility 模式。

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

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

          参与机制应该是一个接口,这样您就可以有专门的代理来做非常不同的事情,并同意只接收消息并根据接口指示传递它们。

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

          【讨论】:

          • 当然责任链会有所帮助,但在我的情况下,我必须允许链的动态构建,因此链不能是固定的顺序。链中的每个环都不能简单地调用下一个环。我必须考虑第三个组件的干预,该组件定义哪个环将是链中的下一个。此外,最好将构建链的所有逻辑封装在单个对象中,而不是将其分散在各种单个决策点中。
          猜你喜欢
          • 2015-07-02
          • 2020-03-23
          • 1970-01-01
          • 2014-03-19
          • 1970-01-01
          • 1970-01-01
          • 2015-06-30
          • 1970-01-01
          • 2012-03-26
          相关资源
          最近更新 更多