Java 基于访问者模式的表达式评估
我一直在尝试使用visitor框架设计一个表达式计算器。 在进入我目前的设计之前,这里是我面临的几个问题Java 基于访问者模式的表达式评估,java,design-patterns,Java,Design Patterns,我一直在尝试使用visitor框架设计一个表达式计算器。 在进入我目前的设计之前,这里是我面临的几个问题 当前的设计与算术运算符配合得很好,但当涉及到eg5>6的逻辑运算符时,它将输出到false它不起作用。由于Visitor是一个通用接口,因此如果我说Visitor,返回值将变成boolean,但参与值是integer,因此我不能将integer作为参数 设计要求每个操作符都有一个类,可以最小化吗 请分享您对设计的意见。它看起来不错还是需要修改 以下是参与课程 这里是访客界面 public i
5>6
的逻辑运算符时,它将输出到false
它不起作用。由于Visitor
是一个通用接口,因此如果我说Visitor
,返回值将变成boolean
,但参与值是integer
,因此我不能将integer作为参数public interface Visitor<T> {
T visit(Expression expression);
T visit(BinaryExpression binaryExpression);
T visit(ConstantExpression expression);
}
对于第一个问题,我认为您不需要将
left
和right
值绑定到ExpressionVisitor
中的t
您可以将N设置为对象
public class ExpressionVisitor<T> implements Visitor<T> {
@Override
public T visit(Expression expression) {
return expression.accept(this);
}
@Override
public T visit(BinaryExpression arithmeticExpression) {
Operator op = arithmeticExpression.getOperator();
Object left = visit(arithmeticExpression.getLeft());
Object right = visit(arithmeticExpression.getRight());
return op.apply(left, right);
}
@Override
public T visit(ConstantExpression expression) {
return (T) expression.getValue();
}
}
据我所知,您需要解析一个文本表达式以生成可以多次计算的内容 当你得到“你可以计算的东西”时,你解析的表达式语言的细节,包括树结构,操作符的种类,等等,都应该考虑在内。不要将这些问题推到只需要评估的代码中 因此,您的解析过程应该生成一个实现如下接口的对象:
interface IExpression {
double evaluateNumeric(Environment env);
boolean evaluateLogical(Environment env);
String evaluateString(Environment env);
boolean isConstant();
}
IExpressionFactory {
IExpression add(IExpression left, IExpression right);
IExpression multiply(IExpression left, IExpression right);
///... etc. etc.
}
在这里,Environment
对象保存变量值、函数定义或任何表达式可以用作输入的内容
如您所见,该接口允许将任何表达式计算为布尔值(例如,如果在if中使用)、数字或字符串。根据您的表达式语言,其中一些可能会抛出不支持操作异常
。例如,对于JavaScript之类的东西,它们几乎总是可以工作的
您还可以使用一些方法来告诉您有关表达式的信息,如isConstant
。根据语言的不同,提供表达式自然生成的对象类型可能很重要
为了避免解析器涉及操作实现问题,您可能希望为其提供如下工厂接口:
interface IExpression {
double evaluateNumeric(Environment env);
boolean evaluateLogical(Environment env);
String evaluateString(Environment env);
boolean isConstant();
}
IExpressionFactory {
IExpression add(IExpression left, IExpression right);
IExpression multiply(IExpression left, IExpression right);
///... etc. etc.
}
解析器将在这里为每个子表达式调用一个方法,以从其子表达式创建它
不同类型的子表达式可以以任何方式实现。在Java中,我通常为每个类别使用带有lambda参数的类,如:
class ExpressionFactory : IExpressionFactory {
...
IExpression add(final IExpression left, final IExpression right) {
return arithmeticBinary(left, right, (a,b)->a+b);
}
...
}
这样就不必为每个操作符编写类。当我非常关心计算速度时,我会使用带有抽象基的内联类
重要提示:请注意,由于编译的IExpression
不会公开解析后创建它的表达式树,因此您不必保留该结构,并且可以执行诸如常量折叠之类的优化:
IExpression arithmeticBinary(IExpression left, IExpression right, DoubleBiFunc operator) {
if (left.isConstant() && right.isConstant()) {
// The operands are constant, so we can evaluate this during compilation
double value = operator.apply(
left.evaluateNumeric(EMPTY_ENV),
right.evaluateNumeric(EMPTY_ENV));
return new NumericConstant(value);
} else {
return new ArithmeticBinaryExpression(left, right, operator);
}
}
通过在编译过程中尽可能多地进行处理,您可以通过取消所有类型/转换检查并执行各种优化来加快计算速度。您正在寻找一个新的方法吗?如果您只想对表达式求值一次,那么您应该在解析表达式时这样做,并且忘记所有这些类。如果您想使用不同的输入多次计算它(但我没有看到任何输入),那么您应该将它编译成支持高效执行的形式。当您想将访问者模式用于未知目的时,您通常会使用它。你确定这就是你想做的吗?@MattTimmermans我想用多个输入来评估它。。我没有把所有的课程都发到这里。可以有形式为
x+y
的表达式,并计算x和y
的不同值。还有一些功能也将被评估。
public class GreaterThan extends Operator{
public Boolean doIntegerGreaterThan(Integer left, Integer right) {
return left > right;
}
public Boolean doDoubleGreaterThan(Double left, Double right) {
return left > right;
}
public Boolean doStringGreaterThan(String left, String right) {
return left.length() > right.length();
}
}
interface IExpression {
double evaluateNumeric(Environment env);
boolean evaluateLogical(Environment env);
String evaluateString(Environment env);
boolean isConstant();
}
IExpressionFactory {
IExpression add(IExpression left, IExpression right);
IExpression multiply(IExpression left, IExpression right);
///... etc. etc.
}
class ExpressionFactory : IExpressionFactory {
...
IExpression add(final IExpression left, final IExpression right) {
return arithmeticBinary(left, right, (a,b)->a+b);
}
...
}
IExpression arithmeticBinary(IExpression left, IExpression right, DoubleBiFunc operator) {
if (left.isConstant() && right.isConstant()) {
// The operands are constant, so we can evaluate this during compilation
double value = operator.apply(
left.evaluateNumeric(EMPTY_ENV),
right.evaluateNumeric(EMPTY_ENV));
return new NumericConstant(value);
} else {
return new ArithmeticBinaryExpression(left, right, operator);
}
}