Java设计模式——解释器模式【Interpreter Pattern】

一、引言

23种设计模式大概分为三大类:

5种(创建型模式):工厂方法模式、抽象工厂模式、单例模式、原型模式、建造者模式。

7种(结构型模式):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

11种(行为型模式):策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式

行为型又可以通过类与类之间的关系进行划分 :
在这里插入图片描述

解释器模式基本介绍:

  • 在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。这里的词法分析器和语法分析器都可以看做是解释器
  • 解释器模式(Interpreter Pattern):是指给定一个语言(表达式),定义它的文法的一种表示,并定义一个解释器,使用该解释器来解释语言中的句子(表达式)
  • 应用场景
    • 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树
    • 一些重复出现的问题可以用一种简单的语言来表达
    • 一个简单语法需要解释的场景

二、解释器模式

1.解释器模式原理类图

在这里插入图片描述

对原理类图的说明:

  • Context: 是环境角色,含有解释器之外的全局信息

  • AbstractExpression: 抽象表达式,声明一个抽象的解释操作interpret(),这个方法为抽象语法树中所有的节点所共享

  • TerminalExpression: 为终结符表达式, 实现与文法中的终结符相关的解释操作

  • NonTermialExpression: 为非终结符表达式,为文法中的非终结符实现解释操作

说明:输入 Context 和 TerminalExpression 信息通过 Client 输入即可

三、具体需求

1.四则运算问题

  • 先输入表达式的形式,比如 a+b+c-d+e, 要求表达式的字母不能重复

  • 再分别输入 a ,b, c, d, e 的值

  • 最后求出结果:如图(简单起见,只考虑加减)

在这里插入图片描述

2.传统方案

传统方式的问题分析:

  • 编写一个方法,接收表达式的形式,然后根据用户输入的数值进行解析,得到结果
  • 问题分析:如果加入新的运算符,比如 * / () 等等,不利于扩展;另外让一个方法来解析会造成程序结构混乱,不够清晰
  • 解决方案:可以考虑使用解释器模式,即:表达式 ->解释器(可以有多种)->结果

3. 解释器模式方案

思路分析 - 类图

在这里插入图片描述

说明:

  • Expression:抽象类
  • VarExpression:针对变量的解释器
  • SymbolExpression:针对符号的解释器(非终结,即它还有子类)

具体实现

// 抽象类表达式:通过HashMap的键值对可以获取各个变量的值ֵ Expression.java
public abstract class Expression {
	//解释公式和数值的关系:key就是公式(表达式,如a + b - c)  参数(如a,b,c..)
	//value就是具体的值ֵ,即最终HashMap中存放的就是各个变量的值,如HashMap {a=10, b=20}
	public abstract int interpreter(HashMap<String, Integer> var);
}

// 变量的解析器 VarExpression.java
public class VarExpression extends Expression {
	private String key; // key=a,key=b,key=c

	public VarExpression(String key) {
		this.key = key;
	}

	// var 得到的就是 {a=10, b=20}
	// interpreter的作用:根据变量的名称返回对应的值ֵ
	@Override
	public int interpreter(HashMap<String, Integer> var) {
		return var.get(this.key);
	}
}

// SymbolExpression.java
/**
 * 抽象运算符号解析器:
 *  1)每个运算符号都只和自己的左右两个数字有关系
 *  2)同时,左右两个数字可能也是一个解析的结果
 *  3)无论何种类型,都是Expression的实现类
 */
public class SymbolExpression extends Expression {
	protected Expression left;
	protected Expression right;

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

	//因为SymbolExpression是让其子类来实现的,因此interpreter()是一个默认实现
	@Override
	public int interpreter(HashMap<String, Integer> var) {
		return 0;
	}
}

//加法解释器 AddExpression.java
public class AddExpression extends SymbolExpression  {
	public AddExpression(Expression left, Expression right) {
		super(left, right);
	}

	//处理“+”
	//var即{a=10,b=20...}(值key)
	public int interpreter(HashMap<String, Integer> var) {
		//super.left.interpreter(var):返回left表达式对应的值,比如a对应的值是10
		//super.right.interpreter(var):返回right表达式对应的值,比如b对应的值是20
		return super.left.interpreter(var) + super.right.interpreter(var);
	}
}

//减法解释器 SubExpression.java
public class SubExpression extends SymbolExpression {
	public SubExpression(Expression left, Expression right) {
		super(left, right);
	}

	//求出left与right表达式相减的结果
	public int interpreter(HashMap<String, Integer> var) {
		return super.left.interpreter(var) - super.right.interpreter(var);
	}
}

// Calculator.java
public class Calculator {
	//定义表达式
	private Expression expression;

	//构造函数传参 并解析
	public Calculator(String expStr) { //如传入的expStr = a+b
		// 安排运算先后顺序
		Stack<Expression> stack = new Stack<>();
		// 表达式拆分成字符数组
		char[] charArray = expStr.toCharArray();//expStr拆分为 [a, +, b]

		Expression left = null;
		Expression right = null;
		//遍历字符数组,即遍历 [a, +, b],针对不同的情况做相应的处理
		for (int i = 0; i < charArray.length; i++) {
			switch (charArray[i]) {
			case '+': 
				left = stack.pop();//先从栈中弹出left => "a"
				right = new VarExpression(String.valueOf(charArray[++i]));// 取出left表达式 => "b"
				stack.push(new AddExpression(left, right));// 根据得到的left和right对象构建 AddExpresson 并加入stack中
				break;
			case '-': // 
				left = stack.pop();
				right = new VarExpression(String.valueOf(charArray[++i]));
				stack.push(new SubExpression(left, right));
				break;
			default: 
				//如果是一个Var(变量),就创建一个VarExpression对象,并push到栈中
				stack.push(new VarExpression(String.valueOf(charArray[i])));
				break;
			}
		}
		//当遍历完整个charArray数组后,stack中就得到了最后的Expression
		this.expression = stack.pop();
	}

	public int run(HashMap<String, Integer> var) {
		//将表达式a+b和var:{a=10,b=20}绑定
		//然后传递给expression的interpreter进行处理/解释执行
		return this.expression.interpreter(var);
	}
}

// ClientTest.java
public class ClientTest {
	public static void main(String[] args) throws IOException {
		String expStr = getExpStr(); // 如:a+b
		HashMap<String, Integer> var = getValue(expStr);// var:{a=10, b=20}
		Calculator calculator = new Calculator(expStr);
		System.out.println("运算结果:" + expStr + "=" + calculator.run(var));
	}

	//获得表达式
	public static String getExpStr() throws IOException {
		System.out.print("请输入表达式:");
		return (new BufferedReader(new InputStreamReader(System.in))).readLine();
	}

	//获得值映射
	public static HashMap<String, Integer> getValue(String expStr) throws IOException {
		HashMap<String, Integer> map = new HashMap<>();

		for (char ch : expStr.toCharArray()) {
			if (ch != '+' && ch != '-') {
				if (!map.containsKey(String.valueOf(ch))) {
					System.out.print("请输入" + String.valueOf(ch) + "的值:");
					String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
					map.put(String.valueOf(ch), Integer.valueOf(in));
				}
			}
		}
		return map;
	}
}

四、注意事项和细节

  • 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
  • 应用场景:编译器、运算表达式计算、正则表达式、机器人等
  • 使用解释器可能带来的问题:解释器模式会引起类膨胀;解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低。(解释器一般用于解决比较复杂的问题,要慎用)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
解释器模式Interpreter Pattern)是一种行为型设计模式,它定义了一种语言文法的表示,并定义了一个解释器,用于解释语言中的句子。它将一个问题分成两个部分:一部分是语言的文法规则,另一部分是解释器,用来解释规则中的句子。解释器模式可以用于处理一些简单的语言,如数学表达式、正则表达式等。 实现方式: 1. 定义抽象表达式类(AbstractExpression),它是所有表达式类的父类,声明了抽象的解释方法。 2. 定义终结符表达式类(TerminalExpression),它实现了抽象表达式类中的解释方法,用于解释语言中的终结符。 3. 定义非终结符表达式类(NonterminalExpression),它也实现了抽象表达式类中的解释方法,用于解释语言中的非终结符。 4. 定义上下文类(Context),它包含了解释器需要的一些全局信息。 5. 客户端使用时,先创建一个上下文对象,然后将需要解释的语言句子作为参数传入解释器对象中,解释器对象将句子解释成相应的结果。 优点: 1. 可扩展性好,增加新的文法规则只需要添加相应的非终结符表达式类即可。 2. 易于实现语法分析。 缺点: 1. 对于复杂的文法规则,解释器模式的类数量可能会很大,增加程序的复杂性。 2. 执行效率较低,因为需要递归调用解释器对象。 适用场景: 1. 可以用于处理一些简单的语言,如数学表达式、正则表达式等。 2. 当语言的文法规则比较复杂时,可以使用解释器模式进行语法分析。 3. 当需要对语言进行增强时,可以使用解释器模式添加新的文法规则。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

止步前行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值