4

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

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

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

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

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

4

5 回答 5

3

对于这种情况,我会尝试对链进行建模,并通过给节点更多的“智能”来将执行的责任转移到链本身。在某种程度上,我会将节点视为命令,因为它们可以自己执行,并且通过具有通用接口,能够创建组合。(顺便说一句,我不是 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. 我假设一个过滤器返回 type 的东西Content,这实际上允许您将一个过滤器链接到另一个过滤器。我想这在你的要求中是正确的,但我不确定。
  2. 对于每个新过滤器,您只需创建一个新类并定义apply方法。
  3. 空对象始终是链的最后一个节点,停止链调用。
  4. 要定义新的“分支节点”,您必须从子类化ConditionalFilter并重新定义该evalCondition方法。我不知道 Java 是否有闭包(我认为没有),但如果有,您可以添加一个condition实例变量并用闭包对其进行参数化,从而避免为每个新条件进行子类化。或者,在 Java 世界中可能有更可接受的解决方法,我只是不知道 :(。
  5. 我假设条件分支是根据content参数决定的。如果您需要更多信息来做出决定,您还可以在apply方法中传递一个上下文对象。根据您的需要,这可以是结构化对象,如果您需要更大的灵活性,也可以只是字典。

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

高温高压

于 2013-01-28T14:25:38.267 回答
2

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

经过一番思考,在我看来复合模式非常适合这里。

  • Component: 你的Filter界面
  • Leafs: 混凝土过滤器
  • Composite:您的“工作流或 FilterChain 接口”

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

于 2013-01-28T23:05:44.493 回答
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

于 2015-10-22T12:44:03.573 回答
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);
    }
  }
}

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

于 2013-01-27T22:46:59.563 回答
0

您在这里需要的是模式责任链

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

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

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

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

于 2013-01-27T23:21:26.767 回答