Gam*_*per 8 java visitor code-design
考虑以下访问者的简单语言解释器.
public interface Visitor{
void visit( VarStat vs);
void visit( Ident i);
void visit( IntLiteral a);
void visit( Sum s);
}
Run Code Online (Sandbox Code Playgroud)
为了完整起见,我添加了一些代码,提供了必要的实现细节(您可以跳过并直接阅读问题).
public interface Visitable{
void accept( Visitor v);
}
public class VarStat implements Visitable{
Ident i;
Exp e;
public VarStat(Ident id, Exp ex){
i = id;
e = ex;
}
public Ident getIdent() { return i; }
public Exp getExp() { return e; }
@Override
public void accept( Visitor v){
v.visit( this);
}
}
public interface Exp extends Visitable{
}
public class Ident implements Exp{
@Override
public void accept( Visitor v){
v.visit( this);
}
}
Run Code Online (Sandbox Code Playgroud)
var语句定义如下:
VarStat ::== var Ident = Exp;
Exp ::== Exp + Exp | IntLiteral | Ident
IntLiteral ::== [0-9]{0,8}
Ident ::== [a-zA-Z]+
Run Code Online (Sandbox Code Playgroud)
一个有效的语言实例
var x = x+y+4;
Run Code Online (Sandbox Code Playgroud)
表示VarStat节点的抽象方式如下:
. _____VarStat _____
. / / | \ \
. / / | \ \
. / / | \ \
. "var" Ident "=" Exp ";"
Run Code Online (Sandbox Code Playgroud)
问题
通常的VisitorPattern应用程序将是
void visit( VarStat vs){
vs.getIdent().accept( this);
vs.getExp().accept( this);
//...
}
Run Code Online (Sandbox Code Playgroud)
但是,因为我知道"Ident"是Ident一种可能的优化类型
void visit( VarStat vs){
visit( vs.getIdent());
vs.getExp().accept( this);
//...
}
Run Code Online (Sandbox Code Playgroud)
这将跳过2个方法调用来提高性能(实际上它在我的场景中提供了很好的提升).
这被认为是一个可能导致未来问题的设计错误吗?
Visitor只是一个复杂的脚手架,用于在Java等语言上实现双重调度。
当你处理叶子类型时,你不需要双重调度;运行时类型在编译时已知。直接分派叶子类型不仅是一种优化,而且更不符合原则。
当然,问题是,将来叶子类型可能会成为超级类型。对于当今 IDE 中的重构工具来说,这并不是一个大问题。
针对当前的需求进行简单的设计,比针对未知的未来需求进行复杂的设计要好。
在java 8中,我们可以使用非常接近真正的双分派的语法来实现双分派
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){...}
}
Run Code Online (Sandbox Code Playgroud)
另请参阅 - Java Class.cast() 和 Overload