Question

Quelle est la différence entre l'application du modèle de conception de visiteur à votre code et l'approche suivante:

interface Dointerface {
    public void perform(Object o);
}

public class T {
    private Dointerface d;
    private String s;

    public String getS() {
            return s;
    }

    public T(String s) {
            this.s = s;
    }

    public void setInterface(Dointerface d) {
            this.d = d;
    }

    public void perform() {
            d.perform(this);
    }

    public static void main(String[] args) {
            T t = new T("Geonline");
            t.setInterface(new Dointerface() {
                    public void perform(Object o) {
                            T a = (T)o;
                            System.out.println(a.getS());
                    }
            });
            t.perform();
    }
}

Je suppose qu'en utilisant des interfaces, nous ne séparons pas vraiment l'algorithme.

Était-ce utile?

La solution

Deux choses:

  • Dans votre exemple, vous avez besoin de deux méthodes. Le perfom et le setInterface . Avec un modèle de visiteur, vous n’auriez besoin que d’une seule méthode, perfom , généralement appelée accept .
  • Si vous avez besoin de plus d'un "interprète", vous devrez définir l'interprète -par la méthode setInterface pour chacun. Cela rend impossible de rendre votre classe immuable.

Autres conseils

Il y a une grande différence.

Le modèle de visiteur utilise des interfaces, mais son objectif est de pouvoir effectuer une opération sur une ou plusieurs classes (qui implémentent une interface) sans avoir à changer de classe. Par conséquent, la mise en œuvre réellement "visites". la classe et fait sa chose sans que la classe soit modifiée.

Une interface est un concept de base utilisé pour fournir une API commune à un groupe de classes potentiellement divers. Le test type d’une interface est que les classes qui la partagent sont similaires à au moins un égard (is-like-a) et, dans ces cas, peuvent être traitées comme telles.

Voici un exemple simple sur wikipedia qui montre quelques visiteurs en java.

La différence la plus importante dans ces exemples réside dans le fait que vous conservez le type concret de compilation "this" au moment de la compilation. Cela vous permet d'utiliser une double distribution, où la méthode à appeler dépend à la fois du type de données concret et de l'implémentation du visiteur. La double répartition est simplement un cas particulier de répartition multiple où la méthode invoquée dépend du destinataire et des types de paramètres de la méthode. Bien entendu, Java est à répartition unique, mais d’autres langues prennent en charge la répartition multiple.

Le modèle de visiteur repose principalement sur le fait qu'en utilisant des interfaces sur les nœuds concrets, chaque opération devant être ajoutée à une structure de données composite doit changer chaque nœud. Le modèle de visiteur utilise un modèle générique (statique) sur les nœuds, ce qui facilite l'ajout dynamique d'opérations. L'inconvénient est que la modification de la structure de données (par l'ajout ou la suppression de nœuds concrets) devient plus difficile car tous les visiteurs de l'opération sont affectés.

En général, ce compromis est meilleur car il est plus fréquent d'étendre des opérations sur une structure de données que de changer la structure de données elle-même. Voici un article plus long sur la façon d’utiliser les visiteurs et un ensemble de considérations:

Vous pourriez plutôt vous demander s’il existe un modèle nous permettant de faire les deux: ajouter des opérations ou étendre nos structures de données sans détruire le code existant. Ceci est connu sous le nom de «problème d'expression», comme l'a inventé Philip Wadler. Vous pouvez trouver quelques liens sur ceci et plus ici:

Un modèle de visiteur est utilisé lorsque vous avez une structure de données composée de nombreuses classes différentes et que vous avez plusieurs algorithmes qui nécessitent une opération différente pour chaque classe. Dans votre exemple, votre implémentation DoInterface ne fait qu'une opération sur un type. La seule chose que vous fassiez est d’afficher le résultat de getS () et, comme vous le faites de préférence, vous ne pouvez le faire qu’aux classes de type T.

Si vous vouliez appliquer votre interface à une classe de style visiteur classique, votre classe avec votre fonction DoInterface.perform se retrouverait probablement avec une grosse instruction if if if, qui ressemble à ceci:

    public void visit(Object o) {
        if (o instanceof File)
            visitFile((File)o);
        else if (o instanceof Directory)
            visitDirectory((Directory)o);
        else if (o instanceof X)
            // ...
    }

Comme cela utilise Object, il permet aux appelants de n’importe quel type de créer des erreurs qui ne seront visibles qu’au moment de l’exécution. Un visiteur résout ce problème en créant un & # 8220; visitType & # 8221; fonction pour chaque type dans la structure de données. Les classes de la structure de données sont ensuite chargées de connaître la fonction à appeler du visiteur. Le mappage est effectué par chacune des classes de la structure de données qui implémente une fonction d’acceptation qui rappelle ensuite la classe de visiteur. Si la fonction pour le type n'existe pas sur le visiteur, vous obtenez une erreur de compilation. La méthode accept ressemble à ceci:

    @Override
    public void accept(FileSystemVisitor v) {
        v.visitFile(this);
    }

Une partie du problème avec le modèle de visiteur est qu’il faut beaucoup de code pour vraiment le rendre justice dans un échantillon. Je pense que c’est la raison pour laquelle beaucoup de gens ne l’ont pas car il est facile de se laisser distraire par l’autre code. J'ai créé un exemple de système de fichiers simple qui montre, espérons-le, comment utiliser un visiteur plus clairement. Il crée un composite avec certains fichiers et répertoires puis effectue deux opérations sur la hiérarchie. En pratique, vous voudrez probablement plus de deux classes de données et deux opérations pour justifier ce modèle, mais ce n’est qu’un exemple.

public class VisitorSample {
    //
        public abstract class FileSystemItem {
            public abstract String getName();
            public abstract int getSize();
            public abstract void accept(FileSystemVisitor v);
        }
    //  
        public abstract class FileSystemItemContainer extends FileSystemItem {
            protected java.util.ArrayList<FileSystemItem> _list = new java.util.ArrayList<FileSystemItem>();
    //              
            public void addItem(FileSystemItem item)
            {
                _list.add(item);
            }
    //
            public FileSystemItem getItem(int i)
            {
                return _list.get(i);
            }
    //          
            public int getCount() {
                return _list.size();
            }
    //      
            public abstract void accept(FileSystemVisitor v);
            public abstract String getName();
            public abstract int getSize();
        }
    //  
        public class File extends FileSystemItem {
    //
            public String _name;
            public int _size;
    //      
            public File(String name, int size) {
                _name = name;
                _size = size;
            }
    //      
            @Override
            public void accept(FileSystemVisitor v) {
                v.visitFile(this);
            }
    //
            @Override
            public String getName() {
                return _name;
            }
    //
            @Override
            public int getSize() {
                return _size;
            }
        }
    //  
        public class Directory extends FileSystemItemContainer {
    //
            private String _name;
    //      
            public Directory(String name) {
                _name = name;
            }
    //      
            @Override
            public void accept(FileSystemVisitor v) {
                v.visitDirectory(this);
            }
    //
            @Override
            public String getName() {
                return _name;
            }
    //
            @Override
            public int getSize() {
                int size = 0;
                for (int i = 0; i < _list.size(); i++)
                {
                    size += _list.get(i).getSize();
                }
                return size;
            }       
        }
    //  
        public abstract class FileSystemVisitor {
    //      
            public void visitFile(File f) { }
            public void visitDirectory(Directory d) { }
    //
            public void vistChildren(FileSystemItemContainer c) {
                for (int i = 0; i < c.getCount(); i++)
                {
                    c.getItem(i).accept(this);
                }
            }
        }
    //  
        public class ListingVisitor extends FileSystemVisitor {
    //      
            private int _indent = 0;
    //      
            @Override
            public void visitFile(File f) {
                for (int i = 0; i < _indent; i++)
                    System.out.print(" ");
                System.out.print("~");
                System.out.print(f.getName());
                System.out.print(":");
                System.out.println(f.getSize());
            }
    //  
            @Override
            public void visitDirectory(Directory d) {
                for (int i = 0; i < _indent; i++)
                    System.out.print(" ");  
                System.out.print("\\");
                System.out.print(d.getName());
                System.out.println("\\");
    //          
                _indent += 3;
                vistChildren(d);
                _indent -= 3;
            }
        }
    //  
        public class XmlVisitor extends FileSystemVisitor {
    //      
            private int _indent = 0;
    //      
            @Override
            public void visitFile(File f) {
                for (int i = 0; i < _indent; i++)
                    System.out.print(" ");
                System.out.print("<file name=\"");
                System.out.print(f.getName());
                System.out.print("\" size=\"");
                System.out.print(f.getSize());
                System.out.println("\" />");
            }
    //  
            @Override
            public void visitDirectory(Directory d) {
                for (int i = 0; i < _indent; i++)
                    System.out.print(" ");
                System.out.print("<directory name=\"");
                System.out.print(d.getName());
                System.out.print("\" size=\"");
                System.out.print(d.getSize());
                System.out.println("\">");
    //          
                _indent += 4;
                vistChildren(d);
                _indent -= 4;
    //          
                for (int i = 0; i < _indent; i++)
                    System.out.print(" ");
                System.out.println("</directory>");
            }
        }
    //  
        public static void main(String[] args) {
            VisitorSample s = new VisitorSample();
    //      
            Directory root = s.new Directory("root");
            root.addItem(s.new File("FileA", 163));
            root.addItem(s.new File("FileB", 760));
            Directory sub = s.new Directory("sub");
            root.addItem(sub);
            sub.addItem(s.new File("FileC", 401));
            sub.addItem(s.new File("FileD", 543));
            Directory subB = s.new Directory("subB");
            root.addItem(subB);
            subB.addItem(s.new File("FileE", 928));
            subB.addItem(s.new File("FileF", 238));
    //      
            XmlVisitor xmlVisitor = s.new XmlVisitor();
            root.accept(xmlVisitor);
    //      
            ListingVisitor listing = s.new ListingVisitor();
            root.accept(listing);
        }
    }

La seule chose que je vois qui soit évidente, c’est que, en stockant l’interface, vous la rendez ainsi obligée d’effectuer deux opérations plutôt qu’une seule pour l’appeler. Je suppose que cela pourrait avoir un sens si vous exécutez plusieurs fois la même action une fois l'interface configurée, mais je pense que vous pouvez vous en tenir au visiteur standard et accomplir la même chose.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top