0

ジェネリックを使用したビジターパターンの次のJava実装は、有用であるほど一般的ですか?(そうだと思います)。

何らかの方法で改善できますか?匿名クラスを使用して簡単に呼び出し可能であることが重要です。ありがとう。

(使用例):

Vector<Number> numbers = new Vector<Number>();

        numbers.add(new Double(1.2));
        numbers.add(new Float(-1.2));
        numbers.add(new Double(4.8));
        numbers.add(new Float(-3.4));
        numbers.add(new Long(123456));
        numbers.add(new Short("14"));

        For.each(numbers, new Visitor<Number>() {
            public void doIt(Double n) {
                System.out.println("doIt() for double: " + n);
            }
            public void doIt(Float n) {
                System.out.println("doIt() for float: " + n);
            }
            public void doIt(Number n) {
                System.out.println("doIt() for Number: " + n);
            }
        });

        Visitor<Number> visi =  new Visitor<Number>() {
            private StringBuffer  all = new StringBuffer ();
            public void doIt(Number n) {
                System.out.println("doIt() for Number: " + n);
                all.append(n.toString() + " ");
            }
            public Object getResult () {
                return all;
            }
        };

        For.each(numbers, visi);

        System.out.println ("all -> " + visi.getResult());

定義:

//............................................
abstract class Visitor<T> {
    public void visit(T n) {
        try {
            this.getClass().getDeclaredMethod("doIt", n.getClass()).invoke(this, n);
        } catch (Exception ex) {
            doIt((T) n);
        }
    }
    public void doIt(T n) {
        System.out.println("doIt() for base " + n);
    }
    public Object getResult() {
        return null;
    }
} // class

//............................................
class For {
    public static <T> void each (Collection<T> c, Visitor<T> f) {
        for (T v : c) {
            f.visit(v);
        }
    } // ()
} // class
4

3 に答える 3

6

これはビジターパターンではありません。

訪問者はaccept(Visitor v)、訪問者の訪問方法と相互作用する方法を訪問者がパラメータとして取り、さまざまなタイプの訪問者に対して過負荷になり、「ダブルディスパッチ」メカニズムを形成する方法を有することを特徴とする。

デザインパターンの訪問者の「適用性」セクションからの引用:

ビジターパターンを使用する場合
  • オブジェクト構造には、インターフェイスが異なる多くのクラスのオブジェクトが含まれており、具体的なクラスに依存するこれらのオブジェクトに対して操作を実行する必要があります。
  • 多くの別個の無関係な操作をオブジェクト構造内のオブジェクトに対して実行する必要があり、これらの操作でクラスを「汚染」することを避けたいと考えています。ビジターを使用すると、関連する操作を1つのクラスで定義することにより、それらをまとめることができます。オブジェクト構造が多くのアプリケーションで共有されている場合は、Visitorを使用して、それらを必要とするアプリケーションだけに操作を配置します。
  • オブジェクト構造を定義するクラスが変更されることはめったにありませんが、構造に対して新しい操作を定義したい場合がよくあります。オブジェクト構造クラスを変更するには、すべての訪問者へのインターフェイスを再定義する必要がありますが、これにはコストがかかる可能性があります。オブジェクト構造クラスが頻繁に変更される場合は、それらのクラスで操作を定義することをお勧めします。

したがって、このパターンは、複数のタイプのオブジェクトに対する同様の操作を処理するためのものです。あなたの例では、訪問者と呼んでいるオブジェクトは1つのタイプしか処理できません。

リフレクションを使用して複数のタイプを処理するように修正する回答では(ちなみに、質問の編集として、または個別の質問として行う方がよいでしょう)、accept(Visitor v)リフレクションを使用して訪問先のクラスにメソッドを作成することを避けています。同じ目標をある程度達成しますが、やや厄介です。私はまだそれをVisitorの実装と呼ぶことに抵抗します。

ここで記述したスタイルのコードが役立つ場合は、必ずそれを使用してください。ただし、訪問者とは呼ばないでください。

これは、ストラテジーパターン関数オブジェクトに似ています。ジェネリッククラスの名前をそれを反映するように変更すると、実際に役立ちます。使用法は、関数型言語でのリスト処理の一般的なパターンに似ています。

質問のコードで私が行う可能性が高いのは、名前をVisitor<T>toOperation<T>に変更し、名前をvisit(T t)toexecute(T t)またはに変更することです。これは、戻り値のないaとapply(T t)考えます。私は実際、これをあなたがしているのと同じように正確に使用し、ジェネリックオブジェクトを使用したコレクションの「マッピング」に同様の戦術を使用しました。どのパターン名が実際にそれに合うかはわかりませんが、Visitorではありません。これは、関数が自然にファーストクラスのオブジェクトではないオブジェクト指向の世界に関数型リスト内包表記スタイルをもたらします。OperationFunctionFunction<Domain, Range>

于 2010-10-15T22:18:27.090 に答える
2

ビジターパターンを実装していない最初のコードについてのdonrobyの回答のおかげで、この新しいバージョンに到達しました。

これで、accept()メソッドを使用してvisited要素を変更することなく、visitorパターンが実装されると思います。とにかく、リフレクションのおかげで、要素のタイプに応じて適切なメソッドを呼び出すことができます(これがaccept()の使命だと思います)。

まず、使用例:

Vector<Number> numbers = new Vector<Number>();

    numbers.add(new Double(1.2));
    numbers.add(new Float(-1.2));
    numbers.add(new Double(4.8));
    numbers.add(new Float(-3.4));
    numbers.add(new Long(123456));
    numbers.add(new Short("14"));

    For.each(numbers, new Visitor<Number>() {
        public void doIt(Double n) {
            System.out.println("doIt() for double: " + n);
        }
        public void doIt(Float n) {
            System.out.println("doIt() for float: " + n);
        }
        public void doIt(Number n) {
            System.out.println("doIt() for Number: " + n);
        }
    });

これにより、この出力が生成されます

ダブルの場合はdoIt():1.2
floatのdoIt():-1.2
ダブルの場合はdoIt():4.8
floatのdoIt():-3.4
番号のdoIt():123456
番号のdoIt():14

そして最後にコード

abstract class Visitor<T> {
public void visit(T n) {
    try {
        this.getClass().getDeclaredMethod("doIt", n.getClass()).invoke(this, n);
    } catch (Exception ex) {
        doIt((T) n);
    }
}
public void doIt(T n) {
    System.out.println("doIt() for base " + n);
}
public Object getResult() {
    return null;
}

}

class For {
public static <T> void each (Collection<T> c, Visitor<T> f) {
    for (T v : c) {
        f.visit(v);
    }
} // ()

}

于 2010-10-16T10:50:27.610 に答える
0

どうですか

    for(String s : words)
        System.out.println (s.toUpperCase());

    int total = 0;
    for(String s : words)
        total = total + s.length();
    System.out.println (" sum of lengths = " + total);
于 2010-10-15T23:02:59.220 に答える