Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/meteor/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 是跳绳吗;接受;在已知类型的情况下,是否对访问者模式进行了有效的优化?_Java_Visitor_Code Design - Fatal编程技术网

Java 是跳绳吗;接受;在已知类型的情况下,是否对访问者模式进行了有效的优化?

Java 是跳绳吗;接受;在已知类型的情况下,是否对访问者模式进行了有效的优化?,java,visitor,code-design,Java,Visitor,Code Design,考虑下面的访问者作为一个简单的语言解释器 public interface Visitor{ void visit( VarStat vs); void visit( Ident i); void visit( IntLiteral a); void visit( Sum s); } 为了完整起见,我添加了一些代码,提供了必要的实现细节(您可以跳过并直接阅读问题) var语句的定义如下: VarStat ::== var Ident = Exp; Exp ::=

考虑下面的访问者作为一个简单的语言解释器

public interface Visitor{
    void visit( VarStat vs);
    void visit( Ident i);
    void visit( IntLiteral a);
    void visit( Sum s);
}
为了完整起见,我添加了一些代码,提供了必要的实现细节(您可以跳过并直接阅读问题)

var语句的定义如下:

VarStat ::== var Ident = Exp;
Exp ::== Exp + Exp | IntLiteral | Ident
IntLiteral ::== [0-9]{0,8}
Ident ::== [a-zA-Z]+
有效的语言实例

var x = x+y+4;
表示
VarStat
节点的抽象方法如下:

.               _____VarStat _____
.              /       /  | \     \ 
.             /       /   |  \     \  
.            /       /    |   \     \
.         "var"   Ident  "="  Exp   ";"

问题

通常的VisitorPattern应用程序是

void visit( VarStat vs){
     vs.getIdent().accept( this);
     vs.getExp().accept( this);
     //...
}
然而,由于我知道“Ident”属于
Ident
类型,因此可能需要进行优化

void visit( VarStat vs){

     visit( vs.getIdent());
     vs.getExp().accept( this);
     //...
}
这将跳过2个提高性能的方法调用(实际上,在我的场景中,它提供了一个很好的提升)


这是否被视为可能导致未来问题的设计错误?

是否会导致问题?很难说。(我想如果语法改变的话,可能会让人感到惊讶……)

但我认为真正的问题是,这是否是一个有价值的优化。具体来说,保存两个方法调用是否会在整体上产生显著的差异?我的直觉是不会的

这个解释器的性能真的重要吗


如果是,为什么在解释器中使用访问者模式?您不应该编译为中间虚拟机代码吗?或者字节码?

在这种情况下,它不是访问者模式。如果它符合您的需求,则不必如此,访问者经常被误用并导致过度架构


然而,你会失去潜在的好处。例如,在将调用转发到装饰/代理对象之前,您将无法为Ident创建装饰器或代理,也无法在accept方法中执行其他操作。

Visitor只是一个复杂的脚手架,用于在Java等语言上实现双重分派

处理叶类型时,不需要双重分派;运行时类型在编译时已知。直接分派一个叶类型不仅是一种优化,而且更不合原则

当然,问题是,在未来,叶类型可能会变成超级类型。对于当今IDE中的重构工具,这不是一个大问题

与其为未知的未来需求进行复杂的设计,不如为当前的需求进行简单的设计


在Java8中,我们可以使用非常接近真实的双重分派的语法实现双重分派

final DoubleDispatch<Root,Void> dd = new DoubleDispatch<>();

dd.register(X.class, x->
{
    do something with x; its compile time type is X
    return null;
});
dd.register(Y.class, y->
{
    do something with y; its compile time type is Y
    return null;
});
// etc

...
dd.invoke( something );



// ----

public class DoubleDispatch<T, R>
{
    public R invoke(T obj){...}

    public <C extends T> void register(Class<C> type, Function<C,R> func){...}
}
final DoubleDispatch dd=new DoubleDispatch();
dd.register(X.class,X->
{
对x做些什么;它的编译时类型是x
返回null;
});
dd.寄存器(Y.类,Y->
{
用y做点什么;它的编译时类型是y
返回null;
});
//等
...
调用(某物);
// ----
公共类双重分派
{
公共R调用(T obj){…}
公共无效寄存器(类类型,函数func){…}
}

另请参见-

实际上,它在我的场景中提供了一个很好的提升。真正地怎么做?是的,我也想看看测量的数字。快15%。在20个地方使用了超过25种方法(真正的访问者现在有25种方法)。由于堆栈跟踪变得更小,调试也更容易,为什么时间对您很重要?相关-没有访问者模式的双重分派-@Jayan-这是合理的。新代码更容易针对JVM进行优化;它可能是内联的。
final DoubleDispatch<Root,Void> dd = new DoubleDispatch<>();

dd.register(X.class, x->
{
    do something with x; its compile time type is X
    return null;
});
dd.register(Y.class, y->
{
    do something with y; its compile time type is Y
    return null;
});
// etc

...
dd.invoke( something );



// ----

public class DoubleDispatch<T, R>
{
    public R invoke(T obj){...}

    public <C extends T> void register(Class<C> type, Function<C,R> func){...}
}