设计模式-解释器模式(Interpreter Pattern)
原创
©著作权归作者所有:来自51CTO博客作者ITKaven的原创作品,请联系作者获取转载授权,否则将追究法律责任
推荐:Java设计模式汇总
解释器模式
定义
Given a language,define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
类型
行为型。
角色
- AbstractExpression(抽象表达式):在抽象表达式中声明抽象的解释操作,它是所有终结符表达式和非终结符表达式的公共父类。
- TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例。通常在一个解释器模式中只有少数几个终结符表达式类,它们的实例可以通过非终结符表达式组成较为复杂的句子。
- NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式来完成。
- Context(环境类):环境类又称为上下文类,它用于存储解释器之外的一些全局信息,通常它临时存储了需要解释的语句。
例子
这里举计算后缀表达式的例子,为了更加简化,这里只处理两种运算符+
、*
,没有括号,并且默认后缀表达式是正确的。
Interpreter接口(抽象表达式)。
package com.kaven.design.pattern.behavioral.interpreter;
public interface Interpreter {
int interpret();
}
NumberInterpreter类(非终结符表达式),实现了Interpreter接口。
package com.kaven.design.pattern.behavioral.interpreter;
public class NumberInterpreter implements Interpreter {
private int number;
public NumberInterpreter(int number){
this.number = number;
}
public NumberInterpreter(String number){
this.number = Integer.parseInt(number);
}
public int interpret() {
return this.number;
}
}
AddInterpreter类(终结符表达式),实现了Interpreter接口。
package com.kaven.design.pattern.behavioral.interpreter;
public class AddInterpreter implements Interpreter {
private Interpreter firstExpression,secondExpression;
public AddInterpreter(Interpreter firstExpression, Interpreter secondExpression) {
this.firstExpression = firstExpression;
this.secondExpression = secondExpression;
}
public int interpret() {
return this.firstExpression.interpret()+this.secondExpression.interpret();
}
public String toString(){
return "+";
}
}
MultiInterpreter类(终结符表达式),实现了Interpreter接口。
package com.kaven.design.pattern.behavioral.interpreter;
public class MultiInterpreter implements Interpreter {
private Interpreter firstExpression,secondExpression;
public MultiInterpreter(Interpreter firstExpression, Interpreter secondExpression) {
this.firstExpression = firstExpression;
this.secondExpression = secondExpression;
}
public int interpret() {
return this.firstExpression.interpret()*this.secondExpression.interpret();
}
public String toString(){
return "*";
}
}
OperatorUtil类(操作工具类)。
package com.kaven.design.pattern.behavioral.interpreter;
public class OperatorUtil {
public static boolean isOperator(String symbol){
return (symbol.equals("+") || symbol.equals("*"));
}
public static Interpreter getExpressionObject(Interpreter firstExpression ,
Interpreter secondExpression ,
String symbol){
if(symbol.equals("+")){
return new AddInterpreter(firstExpression , secondExpression);
}
else if(symbol.equals("*")){
return new MultiInterpreter(firstExpression , secondExpression);
}
return null;
}
}
KavenExpressionParser类,这里使用了上面实现的解释器模式相关类和Stack
实现了计算后缀表达式的逻辑,计算后缀表达式的算法应该是数据结构的基础吧,这里就不讲了。
package com.kaven.design.pattern.behavioral.interpreter;
import java.util.Stack;
public class KavenExpressionParser {
private Stack<Interpreter> stack = new Stack<Interpreter>();
public int parse(String str){
String[] strItemArray = str.split(" ");
for(String symbol : strItemArray){
if(!OperatorUtil.isOperator(symbol)){
Interpreter numberExpression = new NumberInterpreter(symbol);
stack.push(numberExpression);
System.out.println(String.format("入栈:%d",numberExpression.interpret()));
}
else{
// 是运算符号可以计算
Interpreter firstExpression = stack.pop();
Interpreter secondExpression = stack.pop();
System.out.println(String.format("出栈:%d 和 %d",
firstExpression.interpret(),secondExpression.interpret()));
Interpreter operator = OperatorUtil.getExpressionObject(firstExpression ,
secondExpression , symbol);
System.out.println(String.format("应用运算符: %s",operator));
int result = operator.interpret();
NumberInterpreter resultExpression = new NumberInterpreter(result);
stack.push(resultExpression);
System.out.println(String.format("阶段结果入栈:%d",resultExpression.interpret()));
}
}
int result = stack.pop().interpret();
return result;
}
}
Expression类(环境类),存储后缀表达式。
package com.kaven.design.pattern.behavioral.interpreter;
public class Expression {
private String expression;
public Expression(String expression) {
this.expression = expression;
}
public String getExpression() {
return this.expression;
}
}
应用层代码:
package com.kaven.design.pattern.behavioral.interpreter;
public class Test {
public static void main(String[] args) {
Expression expression = new Expression("6 100 11 + *");
KavenExpressionParser expressionParser = new KavenExpressionParser();
int result = expressionParser.parse(expression.getExpression());
System.out.println("解释器计算结果"+result);
}
}
输出:
入栈:6
入栈:100
入栈:11
出栈:11 和 100
应用运算符: +
阶段结果入栈:111
出栈:111 和 6
应用运算符: *
阶段结果入栈:666
解释器计算结果666
这里便完成了一个简单的解释器模式例子。
适用场景
- 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
- 一些重复出现的问题可以用一种简单的语言进行表达。
- 执行效率不是关键问题。高效的解释器通常不是通过直接解释抽象语法树来实现的,而是需要将它们转换成其他形式,使用解释器模式的执行效率并不高。
优点
- 易于改变和扩展文法。由于在解释器模式中使用类表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
- 实现文法较为容易。在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂,还可以通过一些工具自动生成节点类代码。
缺点
- 解释器模式会引起类膨胀。
- 解释器模式将会导致系统比较复杂, 为维护带来了非常多的麻烦。
- 执行效率低。由于在解释器模式中一般采用了大量的循环和递归调用(我们的例子是使用栈来代替递归),因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
如果有说错的地方,请大家不吝赐教(记得留言哦~~~~)。