设计模式学习-解释器模式

解释器模式

解释器模式用于对某种特定的语句进行分析和执行,当我们需要使用一些简单的语句来实现特定的操作的时候,只需要输入按照语言规则编写的语句,那么通过解释器模式可以按照预先定义的规则进行解析和执行。

定义

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解析语句中的句子。

结构

  • AbstractExpression抽象表达式。终结符表达式和非终结符表达式的父类,定义了可以进行解释的方法。
  • TerminalExpression终结符表达式。抽象表达式的子类,实现了终结符表达式的功能,与非终结符表达式联合实现特定功能。
  • NonterminalExpression非终结符表达式。抽象表达式的子类,一般实现连接功能,可以包含终结符表达式和非终结符表达式,一般有递归功能。
  • Context环境类。又称上下文类,存储需要解释的语句,关联了解释器,负责进行语句解析。
  • 在这里插入图片描述

实例

做了一个实现加减乘除的例子,通过对输入的计算字符串进行分析并获取结果。但是感觉方案设计的不好,比如新增一个解释规则,需要新增一个文法规则类,并且修改大幅度Handler,不符合开闭原则。

package expression;

public interface Node {

	int execute();
}

package expression;

public class Value implements Node {

	private int number;

	public Value(String number) {
		this.number = Integer.valueOf(number);
	}

	@Override
	public int execute() {
		return this.number;
	}

}

package expression;

public abstract class CalNode implements Node {

	protected Node frontNode;

	protected Node afterNode;

	public CalNode(Node frontNode, Node afterNode) {
		super();
		this.frontNode = frontNode;
		this.afterNode = afterNode;
	}
}

package expression;

public class AddNode extends CalNode {

	public AddNode(Node frontNode, Node afterNode) {
		super(frontNode, afterNode);
	}

	@Override
	public int execute() {
		System.out.println("加运算" + frontNode.execute() + "+" + afterNode.execute());
		return this.frontNode.execute() + this.afterNode.execute();
	}

}

package expression;

public class DivNode extends CalNode {

	public DivNode(Node frontNode, Node afterNode) {
		super(frontNode, afterNode);
	}

	@Override
	public int execute() {
		System.out.println("除运算" + frontNode.execute() + "/" + afterNode.execute());
		return this.frontNode.execute() / this.afterNode.execute();
	}

}

package expression;

public class MulNode extends CalNode {

	public MulNode(Node frontNode, Node afterNode) {
		super(frontNode, afterNode);
	}

	@Override
	public int execute() {
		System.out.println("乘运算" + frontNode.execute() + "*" + afterNode.execute());
		return this.frontNode.execute() * this.afterNode.execute();
	}

}

package expression;

public class SubNode extends CalNode {

	public SubNode(Node frontNode, Node afterNode) {
		super(frontNode, afterNode);
	}

	@Override
	public int execute() {
		System.out.println("减运算" + frontNode.execute() + "-" + afterNode.execute());
		return this.frontNode.execute() - this.afterNode.execute();
	}

}

package expression;

import java.util.Objects;
import java.util.Stack;

public class Calculator {

	private String context;

	public String getContext() {
		return context;
	}

	public void setContext(String context) {
		this.context = context;
	}

	/**
	 * 计算加减乘除
	 * 
	 * @return 计算结果
	 */
	public int calculate() {
		String[] nodes = context.split(" ");
		Node node = null;
		Stack<Node> stack = new Stack<>();

		for (int i = 1; i < nodes.length; i++) {
			if (nodes[i].equals("*") || nodes[i].equals("/")) {
				i++;
				continue;
			}
			// 获取计算front变量
			Node front = getFront(nodes, i, stack);
			// 获取计算after变量
			Node after = getAfter(nodes, i, stack);
			// 获取具体计算对象
			Node cal = getNode(front, after, nodes[i]);

			stack.add(cal);
			i++;
		}
		node = stack.pop();
		return node.execute();
	}

	private Node getAfter(String[] nodes, int i, Stack<Node> stack) {
		Stack<Node> mulDivStack = new Stack<>();
		if (i + 2 < nodes.length && ((nodes[i + 2].equals("*") || nodes[i + 2].equals("/")))) {
			return generateMulDivNode(nodes, i + 2, mulDivStack);
		} else {
			return new Value(nodes[i + 1]);
		}
	}

	private Node generateMulDivNode(String[] nodes, int i, Stack<Node> mulDivStack) {
		Node node = null;
		Node front = mulDivStack.size() == 0 ? null : mulDivStack.pop();
		front = front == null ? new Value(nodes[i - 1]) : front;
		Node after = new Value(nodes[i + 1]);
		if (Objects.equals("*", nodes[i])) {
			node = new MulNode(front, after);
		}
		if (Objects.equals("/", nodes[i])) {
			node = new DivNode(front, after);
		}
		if (i + 2 < nodes.length && ((nodes[i + 2].equals("*") || nodes[i + 2].equals("/")))) {
			mulDivStack.add(node);
			node = generateMulDivNode(nodes, i + 2, mulDivStack);
		}
		return node;
	}

	private Node getFront(String[] nodes, int i, Stack<Node> stack) {
		if (i == 1 || stack.size() == 0) {
			return new Value(nodes[i - 1]);
		} else {
			return stack.pop();
		}
	}

	private Node getNode(Node front, Node after, String string) {
		if (Objects.equals("+", string)) {
			return new AddNode(front, after);
		}
		if (Objects.equals("-", string)) {
			return new SubNode(front, after);
		}
		if (Objects.equals("*", string)) {
			return new MulNode(front, after);
		}
		if (Objects.equals("/", string)) {
			return new DivNode(front, after);
		}
		return null;
	}

}

package expression;

public class Test {

	public static void main(String[] args) throws Exception {
		String context = "1 + 2 * 4 - 5 * 6";
		Calculator cal = new Calculator();
		cal.setContext(context);
		System.out.println(cal.calculate());
	}
}

优点

  • 通过面向对象的思想对新定义的语言内容进行解析和执行,每一个节点都是一个对象,可以比较方便的实现一个简单的语言。
  • 每种语言只需要新增一个相对应的解析规则,符合开闭原则。

适用场景

解释器模式在实际中应用较少,因为它维护了大量的类,维护起来十分麻烦,对于语法复杂的语言编写和维护的难度都很高,而且执行的过程中大多运用了递归,执行效率比较低,编写成本高。
在下面的场景中可以考虑使用解释器模式。

  • 可以将一个需要解释执行的语言中的句子标识为一个抽象语法树。
  • 一些重复出现的问题可以用一种简单的语言进行表达。
  • 语言的文法比较简单。(复杂的文法可以考虑使用语法分析程序)
  • 不要求较高的执行效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值