模式说明
解释器用来处理这样一类对象,该对象可不断划分为更小的对象,使得对该对象的处理和对再分之后的部分的处理类似,且对于不断再分后得到到的最小单位,有最终处理。例如输入歌曲解析出简谱,每个曲子可以划分为更短的曲子,但解析是类似的,直至单个音符可以直接输出简谱符号(或按定义好的规则转换)。该模式类似组合模式,但解释器模式处理对象的元素通常比组合模式的要多,且组合模式是对象结构模式,而解释器 模式是类行为模式。在使用上,组合模式在客户端以树的结构将所有节点按层次组合为一个整体再处理。而对与解释器模式,在客户端直接输入整体,处理时再循环或迭代解释(处理) 非终结表达式直到将迭代至终结符表达式,且所有终结符表达式都被解释完毕。
非终结符:可再分为非终结符和终结符的元素,例如“我吃饭”可再分为“我(代词,主语)”,“吃饭(动词,谓语)”,“饭(名词,宾语)”。
终结符:不可再分的语法元素,例如上述的“我”,“吃”,“饭”。
参考:
https://en.wikipedia.org/wiki/Terminal_and_nonterminal_symbols
本示例定义一种关于加法运算的规则,只有符号"a","b","A","B"之间的加减法是合法运算。客户端将输入一些表达式,通过解释器来判断(解释)该表达式是否符合语法定义,合法输出true,不合法输出false。客户端声明一个上下文类Context,将要解释的表达式传入Context的interpret方法中,返回解释结果。Context类内持有一个非终结符表达式实例(以抽象表达式类声明)NonterminalExpression,并通过无参构造器初始化该实例NonterminalExpression的构造器是有参的,参数是终结符表达式实例TerminalExpression。
结构
抽象表达式类
  定义一个抽象解释方法interpret(String expression)。
终结符表达式类
  继承抽象表达式类。持有一个Set<String>泛型的终结符集合。实现抽象表达式类的抽象解释方法interpret。
非终结符表达式类
  继承抽象表达式。持有所有种类的终结符表达式实例(以AbstractExpress类型)。实现抽象表达式类中的抽象解释方法interpret。
环境类(上下文类)
  持有所有种类的终结符集合(以String数组类型),持有一个非终结符实例(以AbstractExpression类型)。维护一个interpret方法,调用非终结符的interpret方法返回解释结果。
代码演示
package com.yukiyama.pattern.behavior;import java.util.HashSet;import java.util.Set;/*** 解释器模式*/public class InterpreterDemo {public static void main(String[] args) {// 声明一个上下文实例Context context = new Context();// 合法表达式,输出“true”System.out.println(context.interpret("a"));// 合法表达式,输出“true”System.out.println(context.interpret("A+b"));// 合法表达式,输出“true”System.out.println(context.interpret("A+b-a+a-B"));// 非法表达式,输出“false”System.out.println(context.interpret("C+a"));// 非法表达式,输出“false”System.out.println(context.interpret("b+a*B"));// 非法表达式,输出“false”System.out.println(context.interpret("3"));}}/*** 抽象表达式类* 定义一个抽象解释方法interpret(String expression)。*/abstract class AbstractExpression{public abstract boolean interpret(String expression);}/*** 终结符表达式类* 继承抽象表达式类。持有一个Set<String>泛型的终结符集合。并通过有参构造器* 初始化。实现抽象表达式类的抽象解释方法interpret。解释过程很简单,判断该* 传入的expression是否包含在其持有的终结符集合里,包含则true,否则false。*/class TerminalExpression extends AbstractExpression{private Set<String> terminals = new HashSet<>();public TerminalExpression(String[] terminals) {for (int i = 0; i < terminals.length; i++) {this.terminals.add(terminals[i]);}}public Set<String> getTerminals() {return terminals;}public void setTerminals(Set<String> terminals) {this.terminals = terminals;}@Overridepublic boolean interpret(String expression) {return terminals.contains(expression);}}/*** 非终结表达式类* 继承抽象表达式。持有所有终结符(以AbstractExpress类型),并通过有参构造器* 传入终结符集合来初始化。本示例定义两种终结符,小写字母"a"和"b"是一种,大写* 字母"A"和"B是另一种。所以本例的非终结符表达式持有两个终结符表达式lowerExp* 和upperExp,通过构造器传入lowerExp和upperExp来初始化。* 实现抽象表达式类中的抽象解释方法interpret。借助已知的合法的运算符号("+","-")* 通过字符串的split方法将原始表达式分割成一个终结符和一个非终结符,对终结符调用* 终结符表达式类的interpret判断,一旦非法立即返回false。对非终结符,递归调用* 非终结符表达式的interpret。*/class NonterminalExpression extends AbstractExpression{private AbstractExpression lowerExp;private AbstractExpression upperExp;public NonterminalExpression(AbstractExpression lowerExp, AbstractExpression upperExp) {this.lowerExp = lowerExp;this.upperExp = upperExp;}@Overridepublic boolean interpret(String exp) {boolean isLegal;// exp多于1个字符时if(exp.length() > 1) {// 将表达式分割为两部分,elements[0]为第一个合法运算符号左边的1个字符// elements[1]为第一个合法运算符号右边的部分String[] elements = exp.split("\\+|\\-", 2);// 判断(解释)elements[0]是否合法isLegal = lowerExp.interpret(elements[0]) || upperExp.interpret(elements[0]);// lements[0]不合法直接返回falseif(!isLegal){return isLegal;}// 递归调用当前非终结表达式实例的interpret方法判断右边部分return this.interpret(elements[1]);} else {// 若表达式exp等于或少于一个字符时,调用终结符的interpret返回解释结果return lowerExp.interpret(exp) || upperExp.interpret(exp);}}}/*** 环境类(上下文类)* 持有所有种类的终结符集合(以String数组类型),持有一个非终结符实例* (以AbstractExpression类型)。无参构造器中初始化非终结符实例,方法如下:* 先通过两个String数组实例化两种终结符,再向非终结符构造参数中传入这两个* 终结符实例。* 维护一个interpret方法,调用非终结符的interpret方法返回解释结果。*/class Context{private String[] lowers = {"a", "b"};private String[] uppers = {"A", "B"};private AbstractExpression exp;public Context() {AbstractExpression lowerExp = new TerminalExpression(lowers);AbstractExpression upperExp = new TerminalExpression(uppers);exp = new NonterminalExpression(lowerExp, upperExp);}public boolean interpret(String expression) {return this.exp.interpret(expression);}}
