使用访问者模式和组合模式构建过滤流。

6

我正在使用复合模式,其中包含多个叶节点类,这些类具有专业操作和访问者模式,以允许执行这些操作。在此示例中,为了清晰起见,我省略了所有明显的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) { }
}

现在可以构建访问者来执行像 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);
    }
}

这是按照您预期使用的。例如:

MultiCommandCollector.streamFor(command).forEach(MultiCommand::decrement);

这有一个显著的限制:在处理流时,它无法用于改变层次结构。例如,以下操作将失败:
CommandListCollector.streamFor(commandList).forEach(cl -> cl.add(command));

我想不出另一个优雅的设计方案来实现这个功能。

我的问题是:是否有自然的扩展方式可以允许一个通用的访问者同时改变层次结构?换句话说,访问者可以在访问一个成员后刷新层次结构再访问下一个吗?这是否与使用流兼容?

1个回答

2
在我的经验中,访问者模式对查询或重新创建层次结构都很有用。查询部分很明显——你只需要监听特定类型的子对象,然后根据需要构建查询结果。另一个问题,改变层次结构,就比较困难了。
在遍历过程中更改层次结构可能会变得非常困难。因此,我知道两种实践中有效的技术。
  1. 在访问层次结构时,构建要更改的对象列表。在访问完成之前不要更改它们。具体的访问者可以将感兴趣的对象列表作为其私有成员构建。一旦访问完成,它将公开对象列表作为其结果。然后开始迭代生成的列表并更改对象。
  2. 在访问层次结构时,当您访问一个元素时,请创建该元素的副本。如果需要更改元素,则构造更改版本。否则,如果元素不需要更改,则返回它作为新元素。完成整个访问后,您将拥有所有按意图进行修改的新层次结构。然后可以取消引用旧层次结构,垃圾收集器将收集那些已被新元素替换的元素。
第一个算法适用于元素是可变的情况。第二个算法适用于元素是不可变的情况。
希望这有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接