泛型在访客模式中过度杀伤 [英] Generics overkill in visitor pattern

查看:100
本文介绍了泛型在访客模式中过度杀伤的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个项目,将十年前编写的旧Java 1.2代码转换为java 7.该项目大量使用特定的访问者。为了保持概念上简单,可以说访问者是这样的:

  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);
//很多方法。

颈部肩部等都是子类一个 BodyPart 抽象类,就像这样:

  public abstract class BodyPart {
//很多字段,所以将其转换为接口并不简单。

public abstract Object accept(RobotVisitor visitor,Object arg);





  // BodyPart的所有子类都是这样实现的:
public class Arm extends BodyPart {
//一些字段,getter和setter ...

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


这些 BodyPart s是分层的。一些 BodyPart 可能包含一些其他 BodyPart s。但它们有时可能包含其他内容。

这位访问者有几个截然不同的实现,而且正如预期的那样,代码被强制转换为残缺。我尝试使用泛型:

  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);
//很多方法。
}

但这不起作用。应用程序传递来自不同类型的参数,并期望同一访问者中的每组方法具有不同的回报。所以,我以类似的方式结束了:

  public interface RobotVisitor  public A visitHead(Head p,B arg); 
public A visitNeck(Neck p,B arg);
public A visitShoulder(肩膀P,C arg);
public A visitArm(Arm p,C arg);
public visitHand(Hand p,C arg);
public D visitFinger(Finger p,E arg);
public F visitChest(Chest p,B arg);
public visitLeg(Leg p,G arg);
public visit Foot(Foot p,G arg);
public H visitToe(Toe p,I arg);
//很多方法。
}

这只会使泛型变得荒谬的矫枉过正,使得界面非常难以使用。

我尝试在子接口中划分接口,分组方法需要相同的参数和相同的返回类型,并且在某些地方有效,但缺点是要移除将方法从 BodyPart 类接收到类似 BodyPart s。

然后,我遇到了一个很大的失败,有一个特定的访问者实现有一个带有 BodyPart -type参数调用接受方法。由于我不再有超类中的接受,这显然是不好的方法。



访问者的实现与参数和访问者的返回类型差异很大。有时参数和返回类型是 BodyPart s,有时是 Void ,有时是 String 整数,有时是摆动组件,有时是其他不相关的对象。然而,在每个访问者中,访问类似 BodyPart 的方法都倾向于获得类似的参数和返回类型。



客户端代码总是只调用头部中的 accept ,仅此而已。所有其他的 accept 方法都是从访问者自己调用的。



我应该怎么做才能使接口泛型,而不是在仿制药过度转变它?现在,我只是在寻找普通 BodyPart 的方法中添加了很多 instanceof ,它们只是简单的如果你真的想要重构,我的 建议将如下所示:



使用数据容器类型传递参数和返回值。在我的评论中,我建议使用 VisitorParameterType VisitorReturnType ,但由于有很多重叠,您可以使用一个通用数据类型。

  public class VisitorData {
private A a;
私人B b;
私人C c;
私人D d;
//每个类型一个构造函数
private VisitorData(A a){
this.a = a;

// getters,setters
}

code>访客

  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);
....
//很多方法。

$ / code>

基类:

  public abstract class BodyPart {
//很多字段,因此将其转换为接口并不简单。

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

$ / code $ / $ p

一个子类:

< pre $ public class Arm extends BodyPart {
//一些字段,获取者和设置者...

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


code


$ b

这个主要的成就不是泛型的引入,而是为了重构您的代码来实现统一访问者模式,这更容易遵循。此外,你摆脱了令人讨厌的未经检查的铸件。


I am working on a project where I am converting an old java 1.2 code written ten years ago to java 7. That project heavily (over)uses a particular visitor. To keep things conceptually simple, lets say that the visitor is something like this:

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.
}

The classes Head, Neck, Shoulder, Arm, etc are all subclasses of a BodyPart abstract class that is something like this:

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);
    }
}

These BodyParts are hierachical. Some BodyParts may contain some other BodyParts. But they sometimes might contain something else.

There are several very distinct implementations of that visitor, and as expected, the code is crippled with casts. I tried to use generics:

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.
}

But this does not works. The application passes parameters from different types and expects different returns for each group of methods in a same visitor. So, I ended with something like that:

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.
}

This simply makes the generics a ridiculous overkill, making the interface very hard to use.

I tried to divide the interface in subinterfaces, grouping methods that expects the same parameter and the same return type and that worked in some places, but the downside was to remove the accept method from the BodyPart class to subclasses that groups similar BodyParts.

Then, I hit a big failure, there was one particular visitor implementation that had a method with a BodyPart-typed parameter calling the accept method in it. Since I no more had the accept in the superclass it was clearly the bad way to do it.

The different implementations of the visitors are greatly varied, as are the parameters and return types in the visitor. Sometimes the parameters and return types are BodyParts, sometimes are Void, sometimes are String and Integer, sometimes are swing components and sometimes are other unrelated objects. However in each visitor, methods visiting similar BodyParts have a tendency to get similar parameters and return types.

The client code always calls just the accept in the Head and nothing more. All the other accept methods are called from the visitor to itself.

What should I do to try to make that interface generic without transforming it in a generics-overkill? For now I simply added a lot of instanceof's in the methods that were looking for a plain BodyPart, which simply defeats the entire point of using the visitor pattern.

解决方案

If you really want to refactor, my recommendations would be like this:

Use a data container type for passing parameters and return values. In my comment I recommended to use a VisitorParameterType and a VisitorReturnType, but since there is much overlapping, you can use one common datatype.

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
}

The 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.
}

The base class:

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);
}

One subclass:

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

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

The main achievement with this was not the introduction of generics, but to refactor your code to implement a uniform visitor pattern, which is much easier to follow. Also, you got rid of the nasty unchecked castings.

这篇关于泛型在访客模式中过度杀伤的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆