私は、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
です。Shoulder
Arm
BodyPart
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
スーパークラスにいなかったので、それは明らかにそれをするのに悪い方法でした。
ビジターのパラメーターとリターンタイプと同様に、ビジターのさまざまな実装は大きく異なります。パラメータとリターンタイプがBodyPart
sである場合もあれVoid
ば、である場合もString
ありInteger
、である場合もあり、swingコンポーネントである場合もあり、その他の無関係なオブジェクトである場合もあります。ただし、各訪問者で、同様のを訪問するメソッドBodyPart
は、同様のパラメーターと戻り値タイプを取得する傾向があります。
クライアントコードは常にinだけを呼び出しaccept
、Head
それ以上は呼び出しません。他のすべてのaccept
メソッドは、訪問者からそれ自体に呼び出されます。
ジェネリックスオーバーキルに変換せずに、そのインターフェイスをジェネリック化するにはどうすればよいですか?今instanceof
のところ、プレーンを探していたメソッドに多くのを追加したBodyPart
だけです。これは、ビジターパターンを使用するという全体的なポイントを単純に打ち負かします。