3

私は、10年前に作成された古いJava1.2コードをJava7に変換するプロジェクトに取り組んでいます。そのプロジェクトは、特定の訪問者を多用します。概念的に単純にするために、訪問者は次のようなものであるとしましょう。

public interface RobotVisitor {
    public Object visitHead(Head p, Object arg);
    public Object visitNeck(Neck p, Object arg);
    public Object visitShoulder(Shoulder p, Object arg);
    public Object visitArm(Arm p, Object arg);
    public Object visitHand(Hand p, Object arg);
    public Object visitFinger(Finger p, Object arg);
    public Object visitChest(Chest p, Object arg);
    public Object visitLeg(Leg p, Object arg);
    public Object visitFoot(Foot p, Object arg);
    public Object visitToe(Toe p, Object arg);
    // A lot of methods.
}

クラスHead、、、、などはすべて、次のような抽象クラスのサブクラスNeckです。ShoulderArmBodyPart

public abstract class BodyPart {
    // A lot of fields, so it is not simple to convert this to an interface.

    public abstract Object accept(RobotVisitor visitor, Object arg);
}
// All the subclasses of BodyPart are implemented like this:
public class Arm extends BodyPart {
    // Some fields, getters and setters...

    public Object accept(RobotVisitor visitor, Object arg) {
        return visitor.visitArm(this, arg);
    }
}

これらBodyPartは階層的です。一部BodyPartのは他のを含む場合がありますBodyPart。ただし、他のものが含まれている場合もあります。

その訪問者にはいくつかの非常に明確な実装があり、予想どおり、コードはキャストで機能していません。私はジェネリックを使おうとしました:

public interface RobotVisitor<R, E> {
    public R visitHead(Head p, E arg);
    public R visitNeck(Neck p, E arg);
    public R visitShoulder(Shoulder p, E arg);
    public R visitArm(Arm p, E arg);
    public R visitHand(Hand p, E arg);
    public R visitFinger(Finger p, E arg);
    public R visitChest(Chest p, E arg);
    public R visitLeg(Leg p, E arg);
    public R visitFoot(Foot p, E arg);
    public R visitToe(Toe p, E arg);
    // A lot of methods.
}

しかし、これは機能しません。アプリケーションは、さまざまなタイプのパラメーターを渡し、同じ訪問者のメソッドのグループごとに異なるリターンを期待します。だから、私はそのようなもので終わりました:

public interface RobotVisitor<A, B, C, D, E, F, G, H, I> {
    public A visitHead(Head p, B arg);
    public A visitNeck(Neck p, B arg);
    public A visitShoulder(Shoulder p, C arg);
    public A visitArm(Arm p, C arg);
    public A visitHand(Hand p, C arg);
    public D visitFinger(Finger p, E arg);
    public F visitChest(Chest p, B arg);
    public A visitLeg(Leg p, G arg);
    public A visitFoot(Foot p, G arg);
    public H visitToe(Toe p, I arg);
    // A lot of methods.
}

これは単にジェネリックをばかげたやり過ぎにし、インターフェースを非常に使いにくくします。

インターフェイスをサブインターフェイスに分割し、同じパラメータと同じリターンタイプを期待し、いくつかの場所で機能するメソッドをグループ化しようとしましたが、欠点はaccept、クラスからメソッドを削除して、BodyPart類似したsをグループ化するサブクラスにすることBodyPartでした。

次に、大きな失敗に見舞われました。ある特定の訪問者の実装には、メソッドをBodyPart呼び出す-typedパラメーターを持つacceptメソッドがありました。私はもうacceptスーパークラスにいなかったので、それは明らかにそれをするのに悪い方法でした。

ビジターのパラメーターとリターンタイプと同様に、ビジターのさまざまな実装は大きく異なります。パラメータとリターンタイプがBodyPartsである場合もあれVoidば、である場合もStringありInteger、である場合もあり、swingコンポーネントである場合もあり、その他の無関係なオブジェクトである場合もあります。ただし、各訪問者で、同様のを訪問するメソッドBodyPartは、同様のパラメーターと戻り値タイプを取得する傾向があります。

クライアントコードは常にinだけを呼び出しacceptHeadそれ以上は呼び出しません。他のすべてのacceptメソッドは、訪問者からそれ自体に呼び出されます。

ジェネリックスオーバーキルに変換せずに、そのインターフェイスをジェネリック化するにはどうすればよいですか?今instanceofのところ、プレーンを探していたメソッドに多くのを追加したBodyPartだけです。これは、ビジターパターンを使用するという全体的なポイントを単純に打ち負かします。

4

1 に答える 1

2

本当にリファクタリングしたい場合、私の推奨事項は次のようになります。

パラメーターと戻り値を渡すには、データ コンテナー型を使用します。私のコメントでは、 aVisitorParameterTypeと aを使用することをお勧めしましVisitorReturnTypeたが、重複が多いため、共通のデータ型を 1 つ使用できます。

public class VisitorData {
    private A a;
    private B b;
    private C c;
    private D d;
    // one constructor for each type
    private VisitorData(A a) {
        this.a = a;
    }
    // getters, setters
}

Visitor:_

public interface RobotVisitor {
public VisitorData visitHead(Head p, VisitorData arg);
public VisitorData visitNeck(Neck p, VisitorData arg);
public VisitorData visitShoulder(Shoulder p, VisitorData arg);
public VisitorData visitArm(Arm p, VisitorData arg);
....
// A lot of methods.
}

基本クラス:

public abstract class BodyPart {
// A lot of fields, so it is not simple to convert this to an interface.

public abstract VisitorData accept(RobotVisitor visitor, VisitorData arg);
}

1 つのサブクラス:

public class Arm extends BodyPart {
    // Some fields, getters and setters...

    public VisitorData accept(RobotVisitor visitor, VisitorData arg) {
        return visitor.visitArm(this, arg);
    }
}

これによる主な成果は、ジェネリックの導入ではなく、コードをリファクタリングして統一されたビジター パターンを実装したことです。また、厄介なチェックされていないキャスティングを取り除きました。

于 2013-02-08T10:42:31.603 に答える