2016-04-06 3 views
5

J'utilise un modèle composite avec plusieurs classes de nœuds feuille qui ont des opérations spécialisées et un modèle de visiteur pour permettre ces opérations. Dans cet exemple, j'ai omis toutes les méthodes évidentes accept pour plus de clarté.Utilisation de modèles Visiteur et Composite pour créer un flux filtré

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) { } 
} 

Il est maintenant possible de construire des visiteurs d'effectuer des opérations telles que decrement. Cependant, je trouve plus facile de créer un visiteur d'usage général qui ruisselle des objets d'une certaine classe de sorte que toute opération peut être effectuée sur eux:

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); 
    } 
} 

Il est utilisé comme on peut s'y attendre. Par exemple:

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

Ceci a une limitation importante: il ne peut pas être utilisé pour modifier la hiérarchie lorsque le flux est traité. Par exemple, l'échec suivant:

CommandListCollector.streamFor(commandList).forEach(cl -> cl.add(command)); 

Je ne peux pas penser à une conception élégante alternative qui permettrait.

Ma question est la suivante: y a-t-il une extension naturelle à cette conception pour permettre à un visiteur généraliste de modifier la hiérarchie? En d'autres termes, y a-t-il un moyen pour le visiteur de visiter un membre puis de rafraîchir la hiérarchie avant de visiter le suivant? Est-ce compatible avec l'utilisation de flux?

Répondre

0

Dans mon expérience précédente, le modèle de visiteur est utile pour interroger ou pour recréer la hiérarchie. La partie d'interrogation est évidente - vous écoutez simplement pour des types spécifiques de sous-objets et ensuite construisez le résultat de la requête comme il convient. L'autre question, changer la hiérarchie, est plus difficile.

Il peut vraiment être difficile de modifier la hiérarchie tout en itérant à travers elle. Par conséquent, je connais deux techniques utiles qui fonctionnent bien dans la pratique.

  1. Lors de la visite de la hiérarchie, créez la liste des objets à modifier. Ne les changez pas avant la fin de la visite. Le visiteur concret peut créer la liste des objets d'intérêt en tant que membre privé. Une fois qu'il a terminé la visite, il expose la liste des objets comme résultat. Commencez ensuite à parcourir la liste résultante et apportez les modifications aux objets .
  2. Lors de la visite de la hiérarchie, lorsque vous visitez un élément, créez une copie de l'élément. Si l'élément doit être modifié, créez la version modifiée. Sinon, si les éléments n'ont pas besoin d'être modifiés, renvoyez-le simplement en tant que nouvel élément . Une fois la visite terminée, vous aurez la nouvelle hiérarchie avec toutes les modifications effectuées comme prévu. L'ancienne hiérarchie pourrait alors être déréférencée, et le garbage collector recueillera les éléments qui ont été remplacés par des éléments nouveaux .

Le premier algorithme est applicable lorsque les éléments sont mutables. Le deuxième algorithme est applicable lorsque les éléments sont immuables.

Espérons que cela aide.