Java 基于访问者模式的表达式评估

Java 基于访问者模式的表达式评估,java,design-patterns,Java,Design Patterns,我一直在尝试使用visitor框架设计一个表达式计算器。 在进入我目前的设计之前,这里是我面临的几个问题 当前的设计与算术运算符配合得很好,但当涉及到eg5>6的逻辑运算符时,它将输出到false它不起作用。由于Visitor是一个通用接口,因此如果我说Visitor,返回值将变成boolean,但参与值是integer,因此我不能将integer作为参数 设计要求每个操作符都有一个类,可以最小化吗 请分享您对设计的意见。它看起来不错还是需要修改 以下是参与课程 这里是访客界面 public i

我一直在尝试使用visitor框架设计一个表达式计算器。 在进入我目前的设计之前,这里是我面临的几个问题

  • 当前的设计与算术运算符配合得很好,但当涉及到eg
    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);
        }
    }