设计模式之解释器模式

解释器模式是一种行为设计模式,用于定义语言的文法并创建解释器来解析这些语言的句子。文章通过定义、示例和总结,阐述了解释器模式的核心概念,包括抽象表达式、终结符表达式、非终结符表达式和上下文的角色。示例展示了如何用解释器模式匹配年龄在18-60之间的人,以及如何计算简单的加减法表达式。虽然解释器模式具有良好的扩展性,但当解释的类型增多时,可能会导致大量的子类和效率问题。
摘要由CSDN通过智能技术生成


1.定义

解释器模式属于行为模式,定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

定义看上去略显生涩,简单解释一下,其实语言可以理解为编程语言或者表达式,我们需要对语言或者表达式进行解释,搞清楚它要做什么,就需要对语言进行转化,而文法就是他们的的规则和结构,根据文法定义我们的解释器,对语言或者表达式进行解释。而解释器模式就是在我们需要对一些语言进行解释的时候,可以考虑使用它。简单说就是用解释编程语言的方式来解释对象或者表达式的值

比如要计算类似a+b-c 或者a>b这种表达式,就可以将根据它的文法定义一个对应的解释器,用来计算表达式的最终值,解释器模式正是为解决这种问题诞生的一种编程思想。这种思想在普通应用中用的比较少,一般都是在语言编译解释层面使用,而且非常复杂。

主要角色:

  • 抽象表达式角色(Abstract Expression):解释器的顶层接口,定义表达式的解释方法interpret
  • 终结符表达式角色(Terminal Expression):抽象表达式的实现,终结符表达式就是实现类似a<b这种表达式,相当于是最底层的操作
  • 非终结符表达式(Nonterminal Expression):也是抽象表达式的实现,可以理解为中间表达式,比如a>b && c<d
  • 环境角色(Context):上下文对象,一般用来处理解释器需要的数据或者共享的数据,比如参数、中间结果等

UML:
在这里插入图片描述
图片来源:百度图库

2.示例

定义一个Person列表,利用解释器模式匹配年龄在18-60之间的人,其实就是计算一个给定的val,判断18≤val && val≥60表达式成立
首先定义抽象表达式角色,其中包含一个表达式

/**
 * @description: 抽象表达式角色
 * @version: 1.0
 */
public interface AbstractExpression {

    boolean interpret(int val);
}

实现终结符表达式,终结符表达式会有具体的符号与之对应

/**
 * @description: 终结符表达式
 * @version: 1.0
 */
public class TerminalExpression implements AbstractExpression {

    private int n;

    private String symbol;

    public TerminalExpression(int n, String symbol) {
        this.n = n;
        this.symbol = symbol;
    }

    @Override
    public boolean interpret(int val) {
        return symbol.equals("gte") ? val >= n  : (symbol.equals("lte") ? val <= n : Boolean.FALSE);
    }
}

定义非终结符表达式,分别对应与逻辑和或逻辑,非终结符的表达式计算结果依赖于终结符表达式的计算结果

/**
 * @description: 非终结符表达式
 * @version: 1.0
 */
public class AndExpression implements AbstractExpression{

    private AbstractExpression expression1;

    private AbstractExpression expression2;

    public AndExpression(AbstractExpression expression1,AbstractExpression expression2) {
        this.expression1 = expression1;
        this.expression2 = expression2;
    }

    @Override
    public boolean interpret(int val) {
        return expression1.interpret(val) && expression2.interpret(val);
    }
}
/**
 * @description: 非终结符表达式
 * @version: 1.0
 */
public class OrExpression implements AbstractExpression{
    private AbstractExpression expression1;

    private AbstractExpression expression2;

    public OrExpression(AbstractExpression expression1,AbstractExpression expression2) {
        this.expression1 = expression1;
        this.expression2 = expression2;
    }

    @Override
    public boolean interpret(int val) {
        return expression1.interpret(val) || expression2.interpret(val);
    }
}

定义一个上下文对象,这里简单计算表达式的最终结果

/**
 * @description: context
 * @version: 1.0
 */
public class Context {

    public AbstractExpression expression;

    public Context(int left,int right){
        AbstractExpression exp1 = new TerminalExpression(left, "gte");
        AbstractExpression exp2 = new TerminalExpression(right, "lte");
        expression = new AndExpression(exp1, exp2);
    }

    public boolean operation(int age){
        return expression.interpret(age);
    }

}

input:

年龄在18-60岁之间,信息:Person{name='张三1', age=36岁}
年龄在18-60岁之间,信息:Person{name='张三3', age=34岁}
年龄在18-60岁之间,信息:Person{name='张三4', age=29岁}
年龄在18-60岁之间,信息:Person{name='张三5', age=38岁}
年龄在18-60岁之间,信息:Person{name='张三6', age=43岁}
年龄在18-60岁之间,信息:Person{name='张三7', age=24岁}
年龄在18-60岁之间,信息:Person{name='张三9', age=24岁}
年龄在18-60岁之间,信息:Person{name='张三10', age=44岁}
年龄在18-60岁之间,信息:Person{name='张三11', age=19岁}
年龄在18-60岁之间,信息:Person{name='张三15', age=31岁}
年龄在18-60岁之间,信息:Person{name='张三17', age=50岁}
年龄在18-60岁之间,信息:Person{name='张三19', age=44岁}
年龄在18-60岁之间,信息:Person{name='张三20', age=34岁}
年龄在18-60岁之间,信息:Person{name='张三24', age=53岁}
年龄在18-60岁之间,信息:Person{name='张三29', age=28岁}

这个例子写完就有点后悔,有点多此一举之嫌…,表达目的不明确。找了另外一个例子做一下补充,该示例单纯的计算了简单的加减法运算,在context类中利用栈实现了无限表达式树的计算,这里似乎更能够体现context的作用

public interface AbstractExpression {

	// 每个表达式都必须有一个解释操作
	public int interprete(HashMap<String, Integer> var);

}

public class VarExpression implements AbstractExpression {

	private String key;

	public VarExpression(String key) {
		this.key = key;
	}
	public int interprete(HashMap<String, Integer> var) {
		return (Integer) var.get(this.key);
	}
}

public abstract class SymbolExpression implements AbstractExpression {

	protected AbstractExpression left;
	protected AbstractExpression right;

	// 非终结符表达式的解释操作只关心自己左右两个表达式的结果
	public SymbolExpression(AbstractExpression left, AbstractExpression right) {
		this.left = left;
		this.right = right;
	}
}

public class AddExpression extends SymbolExpression {

	public AddExpression(AbstractExpression left, AbstractExpression right) {
		super(left, right);
	}

	// 把左右两个表达式运算的结果加起来
	public int interprete(HashMap<String, Integer> var) {
		return super.left.interprete(var) + super.right.interprete(var);
	}
}

public class SubExpression extends SymbolExpression {

	public SubExpression(AbstractExpression left, AbstractExpression right) {
		super(left, right);
	}
	// 左右两个表达式相减
	public int interprete(HashMap<String, Integer> var) {
		return super.left.interprete(var) - super.right.interprete(var);
	}
}

public class Calculator {

	private AbstractExpression expression;

	public Calculator(String expStr) {
		// 定义一个堆栈,安排运算的先后顺序
		Stack<AbstractExpression> stack = new Stack<AbstractExpression>();
		// 表达式拆分为字符数组
		char[] charArray = expStr.toCharArray();
		// 运算
		AbstractExpression left = null;
		AbstractExpression right = null;
		for (int i = 0; i < charArray.length; i++) {
			switch (charArray[i]) {
			case '+': // 加法
				left = stack.pop();
				right = new VarExpression(String.valueOf(charArray[++i]));
				stack.push(new AddExpression(left, right));
				break;

			case '-': // 减法
				left = stack.pop();
				right = new VarExpression(String.valueOf(charArray[++i]));
				stack.push(new SubExpression(left, right));
				break;
			default: // 公式中的变量
				stack.push(new VarExpression(String.valueOf(charArray[i])));
			}
		}
		// 把运算结果抛出来
		this.expression = stack.pop();
	}

	// 计算结果
	public int calculate(HashMap<String, Integer> var) {
		return this.expression.interprete(var);
	}
}

public class Test {
	public static void main(String[] args) {
		// 构造运算元素的值列表
		HashMap<String, Integer> ctx = new HashMap<String, Integer>();
		ctx.put("a", 10);
		ctx.put("b", 20);
		ctx.put("c", 30);
		ctx.put("d", 40);
		ctx.put("e", 50);
		ctx.put("f", 60);
		Calculator calc = new Calculator("a+b-c");
		int result = calc.calculate(ctx);
		System.out.println(result);
		calc = new Calculator("d-a-b+c");
		result = calc.calculate(ctx);
		System.out.println(result);
	}
}

该示例引自:菜鸟笔记

3.总结

解释器模式对解决类似“表达式解释”这样的工作提供了一个很好的设计思想,优点就是扩展性好,相对容易实现,只需要关注终结表达式和非终结表达式的设计过程即可。其缺点也不难发现,如果需要解释的类型比较多,会出现相当多的表达式子类,而且他们之间的调用关系类似于递归的方式,如果对象过多,也会存在一定的效率问题。

  • 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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值