目次
总结
ホームページ Java &#&チュートリアル Java は、ビジター モードでリフレクションを使用してサンプル コード共有を実装します。

Java は、ビジター モードでリフレクションを使用してサンプル コード共有を実装します。

Mar 23, 2017 am 11:13 AM

コレクション型は、オブジェクト指向のプログラミングで非常に一般的に使用されており、コード関連の問題も引き起こします。例: 「コレクション内のさまざまなタイプのオブジェクトを操作するにはどうすればよいですか? 1 つの方法は、コレクション内の各要素を反復処理し、その型に基づいて特定の操作を実行することです。これは、特にコレクション内の要素の型が分からない場合に印刷する必要があります。コレクション Element の要素を使用するには、次のようなメソッドを記述できます:

public void messyPrintCollection(Collection collection) {
    Iterator iterator = collection.iterator()
    while (iterator.hasNext())
        System.out.println(iterator.next().toString())
}
ログイン後にコピー

これは、Object.toString() メソッドを呼び出してオブジェクトを出力するだけです。しかし、コレクションが を含むベクトルの場合はどうなるでしょうか?ハッシュテーブルですか? コレクションから返されるオブジェクトの型を確認する必要があります:

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());
        }
}
ログイン後にコピー

さて、これで埋め込みコレクション オブジェクトを処理できますが、他のオブジェクトから返される

String

は必要なものではありません。 ? 文字列オブジェクトが引用符で囲まれている場合、Float オブジェクトの後に f を追加すると、コードがさらに複雑になります: rreee コードがすぐに乱雑になるのを回避するにはどうすればよいですか?

Visitor パターン

を実装するには、Visitor インターフェイス を作成し、訪問されたコレクション オブジェクトの Visitable インターフェイスを作成する必要があります。これらの 2 つのインターフェイスはおおよそ次のとおりです。

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());
    }
}
ログイン後にコピー

特定の String クラスの場合、次のように実装できます:

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);
}
ログイン後にコピー
accept メソッドで、さまざまな型に応じてビジター内の対応するメソッドを呼び出します。 :

Visitor の具体的な実装は次のとおりです。次のように:

public class VisitableString implements Visitable
{
    private String value;
    public VisitableString(String string) {
        value = string;
    }
    public void accept(Visitor visitor) {
        visitor.visitString(this);
    }
}
ログイン後にコピー

そのとき、VisitableFloat クラスと VisitableCollection クラスを実装し、適切な訪問者メソッドを呼び出す限り、多数の if-else 構造を含む mesyPrintCollection メソッドを削除し、非常に巧妙な方法で実現できます。同じ関数です。visitCollection() メソッドは Visitable.accept(this) を呼び出し、次に訪問者の正しいメソッドを呼び出します。これは二重ディスパッチです。訪問者は Visitable クラスのメソッドを呼び出します。 Visitor クラス内で visitor.visitString(this)

訪問者を実装すると if-else ステートメントは消えますが、Visitable インターフェイスを実装するクラスにパッケージ化された String と Float という多くの追加コードを導入する必要がありますが、これは一般的に面倒です。 Visitable オブジェクトのみを含むように制限できるため、問題はありません。ただし、VisitableInteger などの新しい Visitable 型を追加する必要がある場合は、さらに多くの作業が必要になります。これは、訪問者パターンの大きな欠点です。新しい Visitor 型を追加する場合は、Visitor インターフェイスと Visitor インターフェイス メソッドを実装するすべてのクラスを変更する必要があります。 Visitor をインターフェイスとして設計することはできません。代わりに、

no-operation

を備えた抽象基本クラスとして Visitor を設計できます。これは、Java GUI のアダプタ クラスに非常に似ています。これを行う場合の問題は、単一の

継承

を使い果たすことになり、一般的な状況として、StringWriter クラスの継承など、他の関数を実装するために継承も使用したい場合があります。これも、Visitable インターフェイスを実装するオブジェクトにのみ正常にアクセスできます。

幸いなことに、Java では訪問者のパターンをより柔軟にすることができ、必要に応じて Visitable オブジェクトを追加できます。それを達成するにはどうすればよいでしょうか?答えは、リフレクションを使用することです。リフレクションを使用する ReflectiveVisitor インターフェイスに必要なメソッドは 1 つだけです:

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");
    }
}
ログイン後にコピー
さて、上記は非常に簡単です。 Visitable インターフェイスは今のところ使用されません。これについては後で説明します。ここで、リフレクションを使用して PrintVisitor クラスを実装します。
public interface ReflectiveVisitor {
    public void visit(Object o);
}
ログイン後にコピー
これで、Visitable ラッパー クラス (プリミティブ型 String と Float をラップする) を使用する必要がなくなりました。 visit() に直接アクセスすると、正しいメソッドが呼び出されます。 visit() の利点の 1 つは、適切と思われるメソッドをディスパッチすることです。これは必ずしもリフレクションを使用するわけではなく、まったく異なるメカニズムを使用する可能性があります。

新しい PrintVisitor クラスには、Collection、String、Float に対応する操作メソッドがあり、処理できない型については、catch ステートメントを通じてキャプチャできます。処理できない型の場合は、visit() メソッドを拡張することで、すべてのスーパークラスの処理を試みることができます。まず、新しいメソッド getMethod(Class c) を追加します。戻り値はトリガーできるメソッドです。クラス c のすべての親クラスとインターフェースを

検索

して、一致するメソッドを見つけます。

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;
}
ログイン後にコピー

这看上去很复杂,实际上并不。大致来说,首先根据传入的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) { }
}
ログイン後にコピー

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

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

public void accept(Visitor visitor) {
    visitor.visitTreeNode(this);
    visitor.visitTreeNode(leftsubtree);
    visitor.visitTreeNode(rightsubtree);
}
ログイン後にコピー

这样,只要修改下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);
}
ログイン後にコピー

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

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

总结

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

以上がJava は、ビジター モードでリフレクションを使用してサンプル コード共有を実装します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Javaの平方根 Javaの平方根 Aug 30, 2024 pm 04:26 PM

Java の平方根のガイド。ここでは、Java で平方根がどのように機能するかを、例とそのコード実装をそれぞれ示して説明します。

Javaの完全数 Javaの完全数 Aug 30, 2024 pm 04:28 PM

Java における完全数のガイド。ここでは、定義、Java で完全数を確認する方法、コード実装の例について説明します。

Java の乱数ジェネレーター Java の乱数ジェネレーター Aug 30, 2024 pm 04:27 PM

Java の乱数ジェネレーターのガイド。ここでは、Java の関数について例を挙げて説明し、2 つの異なるジェネレーターについて例を挙げて説明します。

Javaのアームストロング数 Javaのアームストロング数 Aug 30, 2024 pm 04:26 PM

Java のアームストロング番号に関するガイド。ここでは、Java でのアームストロング数の概要とコードの一部について説明します。

ジャワのウェカ ジャワのウェカ Aug 30, 2024 pm 04:28 PM

Java の Weka へのガイド。ここでは、weka java の概要、使い方、プラットフォームの種類、利点について例を交えて説明します。

Javaのスミス番号 Javaのスミス番号 Aug 30, 2024 pm 04:28 PM

Java のスミス番号のガイド。ここでは定義、Java でスミス番号を確認する方法について説明します。コード実装の例。

Java Springのインタビューの質問 Java Springのインタビューの質問 Aug 30, 2024 pm 04:29 PM

この記事では、Java Spring の面接で最もよく聞かれる質問とその詳細な回答をまとめました。面接を突破できるように。

Java 8 Stream Foreachから休憩または戻ってきますか? Java 8 Stream Foreachから休憩または戻ってきますか? Feb 07, 2025 pm 12:09 PM

Java 8は、Stream APIを導入し、データ収集を処理する強力で表現力のある方法を提供します。ただし、ストリームを使用する際の一般的な質問は次のとおりです。 従来のループにより、早期の中断やリターンが可能になりますが、StreamのForeachメソッドはこの方法を直接サポートしていません。この記事では、理由を説明し、ストリーム処理システムに早期終了を実装するための代替方法を調査します。 さらに読み取り:JavaストリームAPIの改善 ストリームを理解してください Foreachメソッドは、ストリーム内の各要素で1つの操作を実行する端末操作です。その設計意図はです

See all articles