泛型在访客模式中过度杀伤 [英] Generics overkill in visitor pattern
问题描述
我正在开发一个项目,将十年前编写的旧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 BodyPart
s are hierachical. Some BodyPart
s may contain some other BodyPart
s. 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 BodyPart
s.
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 BodyPart
s, 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 BodyPart
s 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屋!