Duš*_*ský 3 java design-patterns visitor
假设我有一个复合层次结构来表示常规表达式,如下所示:
public abstract class Expression {
public abstract void accept(Visitor visitor);
}
public class Identifier extends Expression {
public final String token;
public Identifier(String token) {
this.token = token;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public String getToken() {
return token;
}
}
public class Sequence extends Expression {
private final List<Expression> subExprs;
public Sequence(List<Expression> subExprs) {
this.subExprs = new ArrayList<Expression>(subExprs);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public List<Expression> getSubExprs() {
return subExprs;
}
}
...
public abstract class Visitor {
public abstract void visit(Identifier identifier);
public abstract void visit(Sequence sequence);
}
Run Code Online (Sandbox Code Playgroud)
问题是,如何实现需要爬树并以递归方式计算结果的操作,例如:
假设例如以下访客实施:
public class Serialize extends Visitor {
public void visit(Sequence sequence) {
for (Expression subExpr : sequence.getSubExprs()) {
// here, I don't have any means to access the sub-results
subExpr.accept(visitor);
}
}
...
}
Run Code Online (Sandbox Code Playgroud)
要计算树的任何给定级别的结果,我需要知道以下级别的子结果.理想情况下,我需要accept方法来返回计算结果.但是,这似乎不可能,因为单个操作可能会返回不同类型的结果.
我想到的唯一解决方案是将子结果手动缓存在访问者类的地图中.但这看起来非常麻烦.
访客在这种情况下是一个合适的模式吗?什么是合适的实施?
首先,扩展访问者返回结果.我经常这样做:
public interface JSStatementVisitor<V, E extends Exception> {
public V visitBlock(JSBlock value) throws E;
public V visitVariable(JSVariableStatement value) throws E;
public V visitEmpty(JSEmptyStatement value) throws E;
...
}
Run Code Online (Sandbox Code Playgroud)
这为访问者提供了更大的灵活性.如果您不需要结果或例外,只需使用Void和RuntimeException.
对应方是:
public interface JSStatement extends JSSourceElement {
public <V, E extends Exception> V acceptStatementVisitor(
JSStatementVisitor<V, E> visitor) throws E;
}
Run Code Online (Sandbox Code Playgroud)
(顺便说一句,我更喜欢使用合格的访问方法(比如visitBlock代替visit或acceptStatementVisitor代替accept)来避免可能的命名冲突,以防某些类必须实现多个接口.
或者你可以让你的访客有状态并有一个方法V getResult(),但我不是一个有状态的东西的忠实粉丝.
这是第一部分.您的访问者现在返回值.
接下来,在使用复合材料时,您需要汇总其结果(因为您的访问者必须仅返回一个结果).添加如下方法:
public V aggregate(Iterable<V> values) throws E;
Run Code Online (Sandbox Code Playgroud)
而你在那里.
(我从来没有做过下面的事情,但它最有可能同样有效.)
另一个选择是将一些Callback<V>回调函数传递给visit方法:
public interface JSStatementVisitor<V, E extends Exception> {
public void visitBlock(JSBlock value, Callback<V> result) throws E;
}
Run Code Online (Sandbox Code Playgroud)
相应的是:
public interface JSStatement extends JSSourceElement {
public <V, E extends Exception> void acceptStatementVisitor(
JSStatementVisitor<V, E> visitor, Callback<V> result) throws E;
}
Run Code Online (Sandbox Code Playgroud)
使用第一部分中的"另一个选项",您甚至不需要额外的聚合方法.你可以实现并传递MyAggregatingCallback<V> implements Callback<V>.它会聚合结果,然后允许flush汇总结果V到原始结果callback.
| 归档时间: |
|
| 查看次数: |
532 次 |
| 最近记录: |