Heim > Java > javaLernprogramm > Hauptteil

Java implementiert die gemeinsame Nutzung von Beispielcode mithilfe von Reflektion im Besuchermodus

黄舟
Freigeben: 2017-03-23 11:13:33
Original
1204 Leute haben es durchsucht

Der Sammlungstyp ist in orientiert an ObjektenProgrammierung wird sehr häufig verwendet, was auch einige codebezogene Fragen aufwirft, wie zum Beispiel „Wie bedient man verschiedene Arten von Objekten in der Sammlung?“ „

Ein Ansatz besteht darin, jedes Element in der Sammlung zu durchlaufen und dann spezifische Operationen basierend auf seinem Typ durchzuführen. Dies wird sehr kompliziert sein, insbesondere wenn Sie den Typ der Elemente in der Sammlung nicht kennen. Wenn Sie die Elemente in der Sammlung drucken möchten, können Sie eine Methode wie diese schreiben:

public void messyPrintCollection(Collection collection) {
    Iterator iterator = collection.iterator()
    while (iterator.hasNext())
        System.out.println(iterator.next().toString())
}
Nach dem Login kopieren

Es scheint einfach, einfach die Object.toString()-Methode aufzurufen und das Objekt auszudrucken, oder? Was ist, wenn Ihre Sammlung? Was ist mit einem Vektor, der eine Hashtabelle enthält? Sie müssen den Typ des zurückgegebenen Sammlungsobjekts überprüfen:

public void messyPrintCollection(Collection collection) {
    Iterator iterator = collection.iterator()
    while (iterator.hasNext()) {
        Object o = iterator.next();
        if (o instanceof Collection)
            messyPrintCollection((Collection)o);
        else
            System.out.println(o.toString());
        }
}
Nach dem Login kopieren

Okay, jetzt können Sie verschachtelte Sammlungsobjekte verarbeiten Andere Objekte String ist nicht das, was Sie wollen? Wenn Sie dem String-Objekt Anführungszeichen hinzufügen und nach dem Float-Objekt ein f hinzufügen möchten, was sollten Sie komplexer tun? >

public void messyPrintCollection(Collection collection) {
    Iterator iterator = collection.iterator()
    while (iterator.hasNext()) {
        Object o = iterator.next();
        if (o instanceof Collection)
            messyPrintCollection((Collection)o);
        else if (o instanceof String)
            System.out.println("'"+o.toString()+"'");
        else if (o instanceof Float)
            System.out.println(o.toString()+"f");
        else
            System.out.println(o.toString());
    }
}
Nach dem Login kopieren
Der Code wird sehr schnell chaotisch. Wie kann Ihnen das

Besuchermuster helfen? >Um das Besuchermuster zu implementieren, müssen Sie eine Visitor-Schnittstelle und eine Visitable-Schnittstelle für das besuchte Sammlungsobjekt erstellen. Als nächstes müssen Sie spezifische Klassen erstellen, um diese beiden Schnittstellen zu implementieren sind ungefähr wie folgt:

Für eine bestimmte String-Klasse kann es wie folgt implementiert werden:

public interface Visitor
{
    public void visitCollection(Collection collection);
    public void visitString(String string);
    public void visitFloat(Float float);
}
public interface Visitable
{
    public void accept(Visitor visitor);
}
Nach dem Login kopieren
Rufen Sie in der Accept-Methode den Besucher entsprechend verschiedenen Typen auf Methode in:

public class VisitableString implements Visitable
{
    private String value;
    public VisitableString(String string) {
        value = string;
    }
    public void accept(Visitor visitor) {
        visitor.visitString(this);
    }
}
Nach dem Login kopieren

Die spezifische Implementierung von Visitor lautet wie folgt:

visitor.visitString(this)

Zu diesem Zeitpunkt, solange die VisitableFloat-Klasse und die VisitableCollection-Klasse vorhanden sind implementiert und die entsprechenden Aufrufe der Besuchermethode durchgeführt werden, können Sie die Methode messyPrintCollection, die eine Reihe von if-else-Strukturen enthält, entfernen und dieselbe Funktion auf sehr erfrischende Weise implementieren. Die Methode visitCollection() ruft Visitable.accept(this auf ) und die Methode „accept()“ wird wiederum im Besucher aufgerufen. Dies ist ein doppelter Versand: Der Besucher ruft eine Methode in der Klasse „Besucher“ auf , Die if-else-Anweisung ist weg, aber sie führt immer noch viel zusätzlichen Code ein. Sie müssen die ursprünglichen Objekte, String und Float, in eine Klasse einbinden, die die Visitable-Schnittstelle implementiert Sie können die besuchte Sammlung so beschränken, dass sie nur besuchbare Objekte enthält. Dies ist eine Menge zusätzlicher Arbeit. Schlimmer noch: Was tun Sie, wenn Sie einen neuen Visitable-Typ hinzufügen möchten, z. B. VisitableInteger? Dies ist ein großer Nachteil des Besucherverhaltens. Wenn Sie einen neuen Visitable-Typ hinzufügen möchten, müssen Sie die Visitor-Schnittstelle und jede Klasse ändern, die die Visitor-Schnittstellenmethoden implementiert. Sie können Visitor nicht als Schnittstelle entwerfen, sondern als abstrakte Basisklasse mit

Keine Operation

. Dies ist der Adapter-Klasse in der Java-GUI sehr ähnlich. Das Problem dabei ist, dass Sie die einzelne
public class PrintVisitor implements Visitor
{
    public void visitCollection(Collection collection) {
        Iterator iterator = collection.iterator();
        while (iterator.hasNext()) {
            Object o = iterator.next();
            if (o instanceof Visitable)
                ((Visitable)o).accept(this);
    }
    public void visitString(String string) {
        System.out.println("'"+string+"'");
    }
    public void visitFloat(Float float) {
        System.out.println(float.toString()+"f");
    }
}
Nach dem Login kopieren
Vererbung

erschöpfen und eine häufige Situation besteht darin, dass Sie die Vererbung auch zum Implementieren anderer Funktionen verwenden möchten, beispielsweise zum Erben der StringWriter-Klasse. Dies wiederum kann nur erfolgreich auf Objekte zugreifen, die die Visitable-Schnittstelle implementieren.

Glücklicherweise kann Java Ihr Besuchermuster flexibler gestalten, Sie können besuchbare Objekte nach Wunsch hinzufügen. Wie erreicht man es? Die Antwort ist Reflexion. Die Schnittstelle „ReflectiveVisitor“ mit Reflektion erfordert nur eine Methode:

Okay, das Obige ist sehr einfach. Die Visitable-Schnittstelle wird vorerst nicht verwendet, ich werde später darüber sprechen. Jetzt implementiere ich die PrintVisitor-Klasse mithilfe von Reflection. Jetzt müssen Sie die Visitable-Wrapper-Klasse (die die primitiven Typen String und Float umschließt) nicht mehr verwenden. Sie können direkt auf visit() zugreifen und es ruft die richtige Methode auf. Ein Vorteil von visit() besteht darin, dass es die Methode auslöst, die es für richtig hält. Dies erfordert nicht unbedingt Reflexion, sondern könnte einen völlig anderen Mechanismus verwenden.

In der neuen PrintVisitor-Klasse gibt es Operationsmethoden, die Collections, String und Float entsprechen. Für Typen, die nicht verarbeitet werden können, können sie durch Catch-Anweisungen erfasst werden. Bei Typen, die nicht verarbeitet werden können, können Sie versuchen, alle ihre Oberklassen zu verarbeiten, indem Sie die Methode visit() erweitern. Fügen Sie zunächst eine neue Methode getMethod (Klasse c) hinzu, und der Rückgabewert ist eine Methode, die ausgelöst werden kann. Es wird

alle übergeordneten Klassen und Schnittstellen der
public interface ReflectiveVisitor {
    public void visit(Object o);
}
Nach dem Login kopieren
Klasse c durchsuchen, um eine passende Methode zu finden.

protected Method getMethod(Class c) {
    Class newc = c;
    Method m = null;
    // Try the superclasses
    while (m == null && newc != Object.class) {
        String method = newc.getName();
        method = "visit" + method.substring(method.lastIndexOf('.') + 1);
        try {
            m = getClass().getMethod(method, new Class[] {newc});
        } catch (NoSuchMethodException e) {
            newc = newc.getSuperclass();
        }
    }
    // Try the interfaces.  If necessary, you
    // can sort them first to define 'visitable' interface wins
    // in case an object implements more than one.
    if (newc == Object.class) {
        Class[] interfaces = c.getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            String method = interfaces[i].getName();
            method = "visit" + method.substring(method.lastIndexOf(&#39;.&#39;) + 1);
            try {
                m = getClass().getMethod(method, new Class[] {interfaces[i]});
            } catch (NoSuchMethodException e) {}
        }
    }
    if (m == null) {
        try {
            m = thisclass.getMethod("visitObject", new Class[] {Object.class});
        } catch (Exception e) {
            // Can&#39;t happen
        }
    }
    return m;
}
Nach dem Login kopieren

这看上去很复杂,实际上并不。大致来说,首先根据传入的class名称搜索可用方法;如果没找到,就尝试从父类搜索;如果还没找到,就从接口中尝试。最后,(仍没找到)可以使用visitObject()作为默认方法。

由于大家对传统的访问者模式比较熟悉,这里沿用了之前方法命名的惯例。但是,有些人可能注意到,把所有的方法都命名为“visit”并通过参数类型不同来区分,这样更高效。然而,如果你这么做,你必须把visit(Object o)方法的名称改为其他,比如dispatch(Object o)。否则,(当没有对应处理方法时),你无法退回到默认的处理方法,并且当你调用visit(Object o)方法时,为了确保正确的方法调用,你必须将参数强制转化为Object。

为了利用getMethod()方法,现在需要修改一下visit()方法。

public void visit(Object object) {
    try {
        Method method = getMethod(getClass(), object.getClass());
        method.invoke(this, new Object[] {object});
    } catch (Exception e) { }
}
Nach dem Login kopieren

现在,visitor类更加强大了——可以传入任意的对象并且有对应的处理方法。另外,有一个默认处理方法,visitObject(Object o),的好处就是就可以捕捉到任何没有明确说明的类型。再稍微修改下,你甚至可以添加一个visitNull()方法。

我仍保留Visitable接口是有原因的。传统访问者模式的另一个好处是它可以通过Visitable对象控制对象结构的遍历顺序。举例来说,假如有一个实现了Visitable接口的类TreeNode,它在accept()方法中遍历自己的左右节点。

public void accept(Visitor visitor) {
    visitor.visitTreeNode(this);
    visitor.visitTreeNode(leftsubtree);
    visitor.visitTreeNode(rightsubtree);
}
Nach dem Login kopieren

这样,只要修改下Visitor类,就可以通过Visitable类控制遍历:

public void visit(Object object) throws Exception
{
    Method method = getMethod(getClass(), object.getClass());
    method.invoke(this, new Object[] {object});
    if (object instanceof Visitable)
    {
        callAccept((Visitable) object);
    }
}
public void callAccept(Visitable visitable) {
    visitable.accept(this);
}
Nach dem Login kopieren

如果你实现了Visitable对象的结构,你可以保持callAccept()不变,就可以使用Visitable控制的对象遍历。如果你想在visitor中遍历对象结构,你只需重写allAccept()方法,让它什么都不做。

当使用几个不同的visitor去操作同一个对象集合时,访问者模式的力量就会展现出来。比如,当前有一个解释器、中序遍历器、后续遍历器、XML编写器以及SQL编写器,它们可以处理同一个对象集合。我可以轻松地为这个集合再写一个先序遍历器或者一个SOAP编写器。另外,它们可以很好地兼容它们不识别的类型,或者我愿意的话可以让它们抛出异常。

总结

使用Java反射,可以使访问者模式提供一种更加强大的方式操作对象结构,可以按照需求灵活地增加新的Visitable类型。我希望在你的编程之旅中可以使用访问者模式。

Das obige ist der detaillierte Inhalt vonJava implementiert die gemeinsame Nutzung von Beispielcode mithilfe von Reflektion im Besuchermodus. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!