返回 导航

其他

hangge.com

Java - GoF设计模式详解13(解释器模式)

作者:hangge | 2023-05-17 08:10

十三、解释器模式

1,基本介绍

(1)解释器模式(Interpreter ):给定一种语言,定义它的文法表示,并定义一个解释器,该解释器根据文法表示来解释语言中的句子。 
(2)解释器模式通常用于处理简单的语言或脚本,解释器模式通常用于以下场景:
  • 当需要实现一个简单的语言或脚本时,可以使用解释器模式。例如,可以使用解释器模式实现简单的表达式求值器,或者实现简单的脚本语言来控制游戏的流程。
  • 当需要对输入的某种语言进行解析时,可以使用解释器模式。例如,可以使用解释器模式来实现 SQL 解析器,将 SQL 语句解析为数据库操作。
  • 当需要对一个复杂的问题进行分解,并使用解释器来解决每个子问题时,可以使用解释器模式。例如,可以使用解释器模式来实现某种算法,将复杂的算法分解成若干个子问题,然后使用解释器来解决每个子问题。
(2)该模式中包含的角色及其职责如下:
  • 抽象表达式(Abstract Expression):定义解释器的接口,约定解释器所包含的操作,比如 interpret() 方法。
  • 终结符表达式(Terminal Expression):抽象表达式的子类,实现与文法中的终结符相关联的解释操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
  • 非终结符表达式(Nonterminal Expression):抽象表达式的子类,文法中的每条规则都需要一个具体的非终结符表达式,非终结符表达式一般是文法中的运算符或其他关键字,比如关系运算符和逻辑运算符,它有两个或多个终结符表达式组成。
  • 环境(Context):存储解释器之外的一些全局信息。

2,使用样例

(1)我们使用解释器模式来实现一个计算器,该计算器能够解析算术表达式并计算结果。首先,我们需要定义一个抽象的表达式(Abstract Expression)类,它声明了一个抽象的解释操作:
// 抽象的表达式(Expression)类
public abstract class Expression {
  public abstract int interpret();
}

(2)然后,我们定义 1 个表示数字的终结符表达式(Terminal Expression)类:
// 表示数字的终结符表达式(Terminal Expression)类
public class NumberExpression extends Expression {
  private int number;

  public NumberExpression(int number) {
    this.number = number;
  }

  @Override
  public int interpret() {
    return number;
  }
}

(3)接着,我们定义四个非终结符表达式(Nonterminal Expression)类,分别表示数字、加号、减号和乘号这四种语法元素。
  • 下面是表示加法的非终结符表达式(Nonterminal Expression)类:
// 加法非终结符表达式
public class PlusExpression extends Expression {
  private Expression left;
  private Expression right;

  public PlusExpression(Expression left, Expression right) {
    this.left = left;
    this.right = right;
  }

  @Override
  public int interpret() {
    return left.interpret() + right.interpret();
  }
}

  • 下面是表示减法的非终结符表达式(Nonterminal Expression)类:
// 减法非终结符表达式
public class MinusExpression extends Expression {
  private Expression left;
  private Expression right;

  public MinusExpression(Expression left, Expression right) {
    this.left = left;
    this.right = right;
  }

  @Override
  public int interpret() {
    return left.interpret() - right.interpret();
  }
}

// 乘法非终结符表达式
public class MultiplyExpression extends Expression {
  private Expression left;
  private Expression right;

  public MultiplyExpression(Expression left, Expression right) {
    this.left = left;
    this.right = right;
  }

  @Override
  public int interpret() {
    return left.interpret() * right.interpret();
  }
}

  • 下面是表示除法的非终结符表达式(Nonterminal Expression)类:
// 除法非终结符表达式
public class DivisionExpression extends Expression {
  private Expression left;
  private Expression right;

  public DivisionExpression(Expression left, Expression right) {
    this.left = left;
    this.right = right;
  }

  @Override
  public int interpret() {
    return left.interpret() / right.interpret();
  }
}

(4)最后我们可以使用以下代码来测试计算器的功能。我们定义了一个包含加减乘除各种运算的表达式,计算器将解析并计算该表达式的值,最终输出  30
public class Test {
  @SneakyThrows
  public static void main(String[] args) {
    // (10-5)*(1+10/2)
    Expression expression = new MultiplyExpression(
            new MinusExpression(new NumberExpression(10), new NumberExpression(5)),
            new PlusExpression(
                    new NumberExpression(1),
                    new DivisionExpression(new NumberExpression(10), new NumberExpression(2)))
            );
    int result = expression.interpret();
    System.out.println(result);  // 输出 30
  }
}

附一:EL 表达式中的解释器模式

(1)Java 中的解释器模式在 javax.el 包中实现。javax.el 包提供了一个表达式语言(Expression Language,简称 EL),可以在 Java 中使用简单的语法来表示各种操作。例如,我们可以使用 EL 表达式来访问 Java 对象的属性、调用方法,以及进行条件判断和循环操作。
(2)在下面的代码中,我们使用 ELProcessor 类来解析 EL 表达式。ELProcessor 类是一个解释器,它能够将 EL 表达式解析为 Java 的运算结果。
public class Test {
  public static void main(String[] args) {
    ELProcessor el = new ELProcessor();
    // 数值计算
    long result = (long)el.eval("1 + 2 * 3");
    System.out.println(result); // prints 7
    // 逻辑运算
    boolean condition = (boolean)el.eval("true && false");
    System.out.println(condition); // prints false
    // 字符串拼接
    String hello = (String)el.eval("'Hello, '.concat('World!')");
    System.out.println(hello); // prints "Hello, World!"
    // bean访问
    Map<String, String> map = new HashMap<>();
    map.put("name", "航歌");
    map.put("age", "100");
    el.defineBean("bean", map);
    String name = (String)el.eval("bean.name");
    System.out.println(name); // prints "航歌!"
  }
}

(3)EL 表达式在 Java Web 应用程序中非常常见,可以在 JSP 文件、JSF 页面和其他类型的模板文件中使用。例如,我们可以在 JSP 文件中使用 EL 表达式来访问 JavaBean 的属性、调用方法,并将结果嵌入到 HTML 文档中。比如下面的 JSP 文件中,我们使用了 EL 表达式 ${bean.greeting()} 来调用 JavaBean greeting() 方法,并将结果嵌入到 HTML 文档中。
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!DOCTYPE html>
<html>
<head>
<title>EL Example</title>
</head>
<body>
    <h1>EL Example</h1>
    <p>Greeting: ${bean.greeting()}</p>
</body>
</html>

附二:Spring 中的解释器模式

(1)Spring 框架中 SpelExpressionParser 使用到了解释器模式。Spring Expression LanguageSpEL)是一种强大的表达式语言,它可以用于在运行时计算表达式的值。SpEL 主要用于在 Spring 框架中的各种地方,如 XML 配置文件和注解中,提供灵活的表达式语言支持。
(2)SpEL 使用类似于 Java 的表达式语法,包括运算符、方法调用和访问对象属性等。它还提供了一些额外的功能,如访问上下文对象、调用静态方法和实例化对象等。下面是一些 SpEL 示例:
  • 计算数学表达式2 + 3 * 5
  • 调用方法'Hello, World!'.substring(6)
  • 访问对象属性person.name
  • 调用静态方法T(java.lang.Math).PI
  • 实例化对象new com.example.Person('John', 'Doe')
public class Test {
  public static void main(String[] args) {
    SpelExpressionParser parser = new SpelExpressionParser();
    // 计算数学表达式:2 + 3 * 5
    Expression expression = parser.parseExpression("2+3*5");
    int result1 = (Integer) expression.getValue();
    System.out.println(result1); //17
    // 调用方法:'Hello, World!'.substring(3)
    expression = parser.parseExpression("'Hello, World!'.substring(3)");
    String result2 = (String) expression.getValue();
    System.out.println(result2);  //lo, World!
  }
}

(3)SpEL 也支持使用 #{} 语法在 XML 配置文件中使用表达式,例如在下面的例子中,SpEL 会在运行时计算表达式 systemProperties.userName 的值,并将结果赋值给 exampleBean name 属性。
<bean id="exampleBean" class="com.example.ExampleBean">
    <property name="name" value="#{systemProperties.userName}" />
</bean>

(4)SpEL 还可以用于 Spring 注解中,例如在下面的例子中,SpEL 会在运行时计算表达式 systemProperties.userName 的值,并将结果赋值给注解所修饰的字段 name
@Value("#{systemProperties.userName}")
private String name;

(5)另外,SpEL 还提供了一些特殊的函数,如:
  • T() 函数用于指定类型,例如 T(java.lang.Math).PI 表示访问 java.lang.Math 类的静态字段 PI
  • null 函数用于返回 null 值,例如 null() 表示返回 null
  • literal() 函数用于将字符串解析为相应的类型,例如 literal('1') 会解析为整数 1

(6)SpEL 还支持一些常用的运算符,包括算术运算符、比较运算符、逻辑运算符、三元运算符等。例如,下面是一些算术运算符的示例:
  • + 加法运算符
  • - 减法运算符
  • * 乘法运算符
  • / 除法运算符
  • % 取模运算符

(7)总之,SpEL 是一种强大的表达式语言,可以用于在运行时计算表达式的值,并且可以在 Spring 框架的各种地方使用。它提供了类似于 Java 的表达式语法,并支持调用方法、访问对象属性、调用静态方法和实例化对象等功能。
评论

全部评论(0)

回到顶部