使用Visitor和Composite模式构建过滤流

spr*_*ter 5 java design-patterns composite visitor-pattern

我正在使用具有多个叶节点类的复合模式,这些类具有专家操作和访问者模式以允许执行这些操作.在这个例子中,accept为了清楚起见,我省略了所有明显的方法.

interface Command {
    public int getCost();
}

class SimpleCommand implements Command {
    private int cost;

    public int getCost() {
        return cost;
    }
}

class MultiCommand implements Command {
    private Command subcommand;
    private int repeated;

    public int getCost() {
        return repeated * subcommand.getCost();
    }

    public void decrement() {
        if (repeated > 0)
            repeated--;
    }
}

class CommandList implements Command {
    private List<Command> commands;

    public int getCost() {
        return commands.stream().mapToInt(Command::getCost).sum();
    }

    public void add(Command command) {
        commands.add(command);
    }
}

interface CommandVisitor {
    default void visitSimpleCommand(SimpleCommandCommand command) { }
    default void visitMultiCommand(MultiCommand multiCommand) { }
    default void visitCommandList(CommandList commandList) { }
}
Run Code Online (Sandbox Code Playgroud)

现在可以建立访问者来执行诸如此类的操作decrement.但是我发现创建一个通用访问者可以更容易地流式传输某个类的对象,以便可以对它们执行任何操作:

class MultiCommandCollector implements CommandVisitor {
    private final Stream.Builder<MultiCommand> streamBuilder = Stream.builder();

    public static Stream<MultiCommand> streamFor(Command command) {
        MultiCommandVisitor visitor = new MultiCommandVisitor();
        command.accept(visitor);
        return visitor.streamBuilder.build();
    }

    public void visitMultiCommand(MultiCommand multiCommand) {
        builder.accept(multiCommand);
    }
}
Run Code Online (Sandbox Code Playgroud)

这可以按照您的预期使用.例如:

MultiCommandCollector.streamFor(command).forEach(MultiCommand::decrement);
Run Code Online (Sandbox Code Playgroud)

这有一个重要的限制:它不能用于在处理流时更改层次结构.例如,以下操作失败:

CommandListCollector.streamFor(commandList).forEach(cl -> cl.add(command));
Run Code Online (Sandbox Code Playgroud)

我想不出一个可以实现这一目标的另类优雅设计.

我的问题是:这个设计有一个自然的扩展,允许一个通用的访问者,也可以改变层次结构?换句话说,有没有办法访问者可以访问一个成员然后刷新层次结构,然后访问下一个?这与流的使用兼容吗?

Zor*_*vat 2

根据我之前的经验,访问者模式对于查询或重新创建层次结构很有用。查询部分是显而易见的 - 您只需侦听特定类型的子对象,然后构建适合的查询结果。另一个问题,即改变层次结构,则更加困难。

在迭代层次结构时更改层次结构可能真的很困难。因此,我知道两种在实践中效果很好的有用技术。

  1. 在访问层次结构时,构建要更改的对象列表。在访问完成之前不要更改它们。具体访问者可以构建感兴趣的对象列表作为其私有成员。一旦完成访问,它将公开对象列表作为其结果。然后才开始迭代结果列表并对对象进行更改。
  2. 在访问层次结构时,当您访问元素时,创建该元素的副本。如果需要更改元素,则构建更改后的版本。否则,如果元素不需要更改,则将其作为新元素返回。整个访问完成后,您将获得新的层次结构,其中所有修改均按预期进行。然后旧的层次结构可以被取消引用,垃圾收集器将收集那些已被新元素替换的元素。

第一种算法适用于元素可变的情况。第二种算法适用于元素不可变的情况。

希望这可以帮助。