设计模式——解释器模式

解释器模式

定义

解释器模式(Interpreter Pattern)是一种按照规定语法进行解析的模式,现实项目中用得较少。
给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。

优缺点、应用场景

优点

解释器是一个简单语法分析工具,最显著的特点就是拓展性。例如:添加一个运算符号的语法解释只需要添加一个类

缺点

  1. 引起类的膨胀。每个语法都要产生一个类,语法规则复杂时,可能产生大量类文件
  2. 采用递归调用,调试不便。
  3. 效率问题。由于使用了大量的循环和递归,当表达式过长且复杂时,可能出现效率问题。

应用场景

  1. 重复发生的问题。例如:多个应用服务器每天会产生大量的日志,数据要素相同但日志格式不同,这种情况就可以使用解释器模式,
  2. 一个简单语法需要解释的场景。期望使用一种(例如:符号)形式描述复杂逻辑,且类间还要进行递归调用的场景,可以考虑使用。

流程

  1. 输入一个表达式,包含值的占位符(a、b、c等)与运算符号(+、-)
  2. 对表达式字符串遍历,根据值的占位符数量要求用户输入对应个数的值,此时要确保每个占位符只能使用一次,不会出现循环赋值的情况
  3. 将值的占位符作为key,对应输入的值作为value,封装到map中
  4. 进入解释器模式的程序,开始运算

代码模拟场景

输入一个计算公式,并根据参数个数与运算符号,输出结果。

解释器模式

UML图

设计模式——解释器模式_第1张图片

表达式抽象、实现

/**
 * 表达式 抽象类
 */
public abstract class Expression {
	/**
	 * 解析公式和数值
	 *
	 * @param map key是公式中的参数,value是参数对应的值
	 * @return 结果
	 */
	public abstract int interpreter(Map<String, Integer> map);

}

/**
 * 变量解析器
 */
public class VarExpression extends Expression {
	private final String key;

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

	@Override
	public int interpreter(Map<String, Integer> map) {
		return map.get(key);
	}
}

/**
 * 抽象运算符号解析器
 */
public abstract class SymbolExpression extends Expression {
	protected Expression left;
	protected Expression right;

	/**
	 * 所有的解析公式都应只关心自己左右两个表达式的结果
	 *
	 * @param left  表达式左部
	 * @param right 表达式右部
	 */
	public SymbolExpression(Expression left, Expression right) {
		this.left = left;
		this.right = right;
	}
}

/**
 * 加法解析器
 */
public class AddExpression extends SymbolExpression {
	/**
	 * 所有的解析公式都应只关心自己左右两个表达式的结果
	 *
	 * @param left  表达式左部
	 * @param right 表达式右部
	 */
	public AddExpression(Expression left, Expression right) {
		super(left, right);
	}

	@Override
	public int interpreter(Map<String, Integer> map) {
		return super.left.interpreter(map) + super.right.interpreter(map);
	}
}

/**
 * 减法解析器
 */
public class SubExpression extends SymbolExpression {
	/**
	 * 所有的解析公式都应只关心自己左右两个表达式的结果
	 *
	 * @param left  表达式左部
	 * @param right 表达式右部
	 */
	public SubExpression(Expression left, Expression right) {
		super(left, right);
	}

	@Override
	public int interpreter(Map<String, Integer> map) {
		return super.left.interpreter(map) - super.right.interpreter(map);
	}
}

解释器封装类

/**
 * 解释器的封装类
 * 将运算符和运算符两边的值进行组合,实现解释
 */
public class Calculator {
	private final Expression expression;

	public Calculator(String expStr) {
		Stack<Expression> stack = new Stack<>();
		char[] cs = expStr.toCharArray();
		Expression left;
		Expression right;
		for (int i = 0; i < cs.length; i++) {
			switch (cs[i]) {
				// 加法
				case '+' -> {
					left = stack.pop();
					right = new VarExpression(String.valueOf(cs[++i]));
					stack.push(new AddExpression(left, right));
				}
				// 减法
				case '-' -> {
					left = stack.pop();
					right = new VarExpression(String.valueOf(cs[++i]));
					stack.push(new SubExpression(left, right));
				}
				default -> stack.push(new VarExpression(String.valueOf(cs[i])));
			}
		}
		this.expression = stack.pop();
	}

	public int run(Map<String, Integer> map) {
		return this.expression.interpreter(map);
	}
}

入口类

/**
 * 解释器模式 入口类
 */
public class ExpressionMain {
	public static void main(String[] args) {
		String expStr = getExpStr();
		Map<String, Integer> map = getValue(expStr);
		Calculator calculator = new Calculator(expStr);
		System.out.println("运算结果为:" + expStr + "=" + calculator.run(map));
	}

	/**
	 * 获取表达式
	 *
	 * @return 控制台输入的表达式
	 */
	private static String getExpStr() {
		System.out.println("输入表达式:");
		try {
			return (new BufferedReader(new InputStreamReader(System.in))).readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}

	/**
	 * 获得值映射
	 */
	public static Map<String, Integer> getValue(String expStr) {
		Map<String, Integer> map = new HashMap<>();
		char[] cs = expStr.toCharArray();
		try {
			for (char c : cs) {
				// 如果是值的占位符
				if (c != '+' && c != '-') {
					// 解决重复参数的问题,确保值的占位符唯一
					if (!map.containsKey(String.valueOf(c))) {
						String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
						map.put(String.valueOf(c), Integer.valueOf(in));
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return map;
	}
}

结果

设计模式——解释器模式_第2张图片

参考书籍

秦小波《设计模式之禅》

你可能感兴趣的:(设计模式,设计模式,解释器模式,java)