Я использую составной шаблон с несколькими классами листовых узлов, у которых есть специальные операции, и шаблон посетителя, позволяющий выполнять эти операции. В этом примере я опустил все очевидные 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));
Я не могу придумать альтернативный элегантный дизайн, который позволил бы это.
Мой вопрос: есть ли естественное расширение этого дизайна, позволяющее посетителю общего назначения также изменять иерархию? Другими словами, есть ли способ, которым посетитель может посетить одного участника, а затем обновить иерархию перед посещением следующего? Совместимо ли это с использованием потоков?