2009-06-03 12 views
2

Alrite, ich bin Gonna Sprung direkt in den Code:Besuchermusterimplementierung in Java - Wie sieht das aus?

public interface Visitor { 

public void visitInventory(); 
public void visitMaxCount(); 
public void visitCountry(); 
public void visitSomethingElse(); 
public void complete(); 
//the idea of this visitor is that when a validator would visit it, it would validate data 
//when a persister visits it, it would persist data, etc, etc. 
// not sure if I making sense here... 
} 

public interface Visitable { 
public void accept(Visitor visitor); 
} 

hier ist eine Basisimplementierung:

public class StoreValidator implements Visitor { 
private List <ValidationError> storeValidationErrors = new ArrayList<ValidationError>(); 

public void addError(ValidationError error) { 
storeValidationErrors.add(error); 
} 

public List<ValidationError> getErrors() { 
return storeValidationErrors; 
} 

public void visitInventory() { 
// do nothing 
} 

public void visitMaxCount() { 
//do nothing 
} 
//... etc.. all empty implementations 

} 

Sie werden sehen, warum ich eine leere Implementierung hier ... ich würde ein schreiben Validator jetzt .., die sich StoreValidator

public XYZValidator extends StoreValidator { 

@Override 
public void visitInventory(Visitable visitable) { 
// do something with visitable .. cast it to expected type 
// invoke a DAO, obtain results from DB 
// if errors found, do addError(new ValidationError()); with msg. 
} 

@Override 
public void visitMaxCount(Visitable visitable) { 
//do something with visitable.. 
} 

// I wouldn't implement the rest coz they wouldn't make sense 
// in XYZValidator.. so they are defined as empty in StoreValidator. 

} 

jetzt ist hier, was ein besuchbar würde wie folgt aussehen:

public Store implements Visitable { 

public void accept(Visitor visitor) { 
visitor.visitInventory(); 
visitor.visitMaxCount(); 
} 
} 

ich haben könnte Code, der so etwas wie diese auf einer Liste von Shop Objekte tut:

List<Store> stores; //assume this has a list of stores. 
StoreValidator validator = new XYZValidator(); //or I would get it from a validatorfactory 
for(Store store: stores) { 
      store.accept(validator); // so even if you send a wrong validator, you are good. 
} 

Ähnlich würden Sie ABCValidator haben, die Umsetzung für andere Methoden (visitCountry/visitSomethinElse) bieten würde, und es würde verlängern von StoreValidator. Ich hätte einen anderen Typ von Objekt (nicht Store), der die Methode accept definiert.

Ich sehe ein Problem hier ... Sag, ich brauche einen FileValidator, der sich von StoreValidator unterscheidet, ich würde erwarten, dass es keine dieser geschäftsbezogenen Validierungen wie visitInventory() usw. hat Eine einzige Schnittstelle Besucher, würde ich schließlich alle Arten von Methoden in Visitor-Schnittstelle deklarieren. Ist das korrekt? Tust du das so?

Ich weiß nicht, ob ich das Muster falsch verstanden habe, oder ob ich einen Sinn habe. Bitte teilen Sie Ihre Gedanken.

Antwort

4

Es gibt ein Problem mit Ihrem Code wie angegeben. Die Schnittstelle Sie verfügt über Methoden geben wie

public void visitInventory(); 

aber Sie setzen es dann in XYZValidator als

public void visitInventory(Visitable visitable) 

Die visitor pattern ist ein Weg multiple dispatch in Sprachen zu implementieren, die nicht automatisch tun (wie Java). Eine der Anforderungen besteht darin, dass Sie eine Gruppe verwandter Klassen (d. H. Eine Gruppe von Unterklassen mit einer einzelnen Oberklasse) haben. Sie haben das hier nicht, also ist das Besuchermuster nicht angemessen. Die Aufgabe, die Sie versuchen, ist jedoch in Ordnung, es ist nur nicht das Besuchermuster.

In Java, sollten Sie von der Besucher-Muster denken, wenn Sie Code haben wie

public void count(Item item) { 
    if (item instanceof SimpleItem) { 
    // do something 
    } else if (item instanceof ComplexItem { 
    // do something else 
    } else ... 
} 

besonders wenn die Subklassen von Artikel sind relativ fixiert.

9

Vor einiger Zeit schrieb ich etwas Ähnliches für meine Masterarbeit. Dieser Code ist leicht Typ sicher als Ihr:

interface Visitable<T extends Visitor> { 

    void acceptVisitor(T visitor); 
} 

interface Visitor { 

    /** 
    * Called before any other visiting method. 
    */ 
    void startVisit(); 

    /** 
    * Called at the end of the visit. 
    */ 
    void endVisit(); 
} 

Beispiel:

interface ConstantPoolVisitor extends Visitor { 

    void visitUTF8(int index, String utf8); 

    void visitClass(int index, int utf8Index); 

    // ==cut== 
} 

class ConstantPool implements Visitable<ConstantPoolVisitor> { 

    @Override 
    public void acceptVisitor(ConstantPoolVisitor visitor) { 
     visitor.startVisit(); 

     for (ConstanPoolEntry entry : entries) { 
      entry.acceptVisitor(visitor); 
     } 

     visitor.endVisit(); 
    } 

also ja, ich denke, dass dies auf jeden Fall ein gutes und flexibles Design, wenn und nur dann, wenn Ihre Daten ändert sich langsamer als dein Verhalten. In meinem Beispiel sind die Daten Java-Bytecode, der fest ist (definiert durch die JVM-Spezifikation). Wenn "Verhalten dominiert" (ich möchte meinen Bytecode dumpen, kompilieren, transformieren, umgestalten usw.), können Sie mit dem Besuchermuster Verhalten ändern/hinzufügen/entfernen, ohne Ihre Datenklassen zu berühren. Fügen Sie einfach eine weitere Implementierung von Visitor hinzu.

Der Einfachheit halber nehme ich an, dass ich meiner Besucherschnittstelle eine weitere Besuchsmethode hinzufügen muss: Ich würde damit enden, meinen gesamten Code zu brechen.

Als Alternative würde ich das Strategie-Muster für dieses Szenario betrachten. Strategy + Decorator ist ein gutes Design für die Validierung.

+0

@dfa Strategie macht Sinn, jedoch würde dies zu einer so viele Strategien wie viele Geschäftsregeln führen.Ich hätte dann so viele Klassen - die wären zu viele. Die Idee hinter dem Besuch eines Besuchers ist, dass ich nicht viel tun sollte, wenn ich meinem Code Verhalten hinzufügen würde. – Jay

+2

"... [wenn] Ihre Daten sich langsamer als Ihr Verhalten ändern." - das ist eine der besten Beschreibungen, die ich in Bezug auf das Besuchermuster gehört habe. +1 – Grundlefleck

1

Ich benutze ein Besuchermuster auf eine andere Art und Weise .. Ich habe eine bestimmte Visitor-Schnittstelle für einen Objekttyp und diese Schnittstelle deklariert nur eine Methode - für den Besuch dieses Objekts ..dies wie:

public interface TreeNodeVisitor { 
    void visit(TreeNode node); 
} 

das Objekt TreeNode kann TreeNodeVisitor s akzeptieren, was bedeutet, dass er es nur nennt Besuch Methode für den Knoten und/oder es ist Kinder ..

Die konkrete Umsetzung der Besucher implementiert die Besuch Methode und sagt, was der Besucher tun wird .. zum Beispiel ContryVisitor, InventoryVisitor, usw.

Dieser Ansatz sollte Ihr Problem vermeiden ..

+0

Das würde bedeuten, ich hätte so viele Besucher Klassen wie viele meine Geschäftsregeln. Dies würde zu einer Klassenexplosion führen. – Jay

1

Wahrscheinlich möchten Sie ein Muster nicht direkt einer einzelnen Schnittstelle zuordnen, die alles nach diesem Muster implementiert. Muster sind KEINE Schnittstellen, sie sind allgemeine Pläne für die Implementierung einer Lösung.

In Ihrem Beispiel würden Sie eine StoreVisitor-Schnittstelle und eine FileVisitor-Schnittstelle für die verschiedenen Geschäftsobjekte erstellen, die das Visitor-Muster unter den entsprechenden Umständen verwenden möchten.

Es könnte sein, dass verschiedene Visitor-Implementierungen gemeinsame Aktivitäten teilen - Sie könnten also ein Superinterface haben, das diese allgemeinen Funktionen definiert. Sie könnten dann Visitable-Schnittstellen codieren, um entweder die spezifische Visitable-Schnittstelle oder die entsprechende Oberklasse zu verwenden.

Beispielsweise können die Schnittstellen FileVisitor und SQLTableVisitor eine Unterklasse einer DataStoreVisitor-Schnittstelle sein. Dann:

VisitableStor eine StoreVisitor nimmt,

VisitableFile akzeptiert eine Filevisitor oder

VisitableDataStor akzeptieren eine DataStoreVistor (die eine Implementierung entweder FileVisitor oder SQLTableVisitor sein könnten).

  • verzeihen Sie die zufälligen Beispiele, ich hoffe, das macht Sinn.
Verwandte Themen