前言
解释器模式使用频率比较少,顾名思义解释器模式就是给定一个语言,定义它的语法,然后解释器根据协议来解释这个语言,得到结果。类似于翻译吧。我觉得用的最多的就是计算表达式了:“A+B”,解释器就是按着操作符区分变量和运算符,然后得到A+B的结果。
适用模式
某个语言需要解析执行,比如解析XML之类的,可以用到解释器模式。再者如果特定场景不断重复这个场景,可以提取语法,抽象出解释其模式。
角色扮演
AbstractExpression: 抽象表达式,定义一个抽象解释方法,子类来实现这个解释方法。
TerminalExpression: 终结符表达式,实现语句里与终结符有关的操作。
NoTerminalExpression:与终结符表达式对应,其实不用在意这个解释,其实就会解释器对不同类型的数据进行不同的处理,仅此而已。
Demo实现:
我们抽象出一个Demo,用于计算表达式“a + b - c”,为了简单拆分表达式的元素,我们以空格为表达式分隔符。
AbstractExpression抽象类:
package com.demo.interpreter;
/**
* Created by Daybreak on 2017/12/19.
*/
public abstract class AbstractExpression {
/**
* 提取解释器公用方法
* @return 解析结果
*/
public abstract int result();
}
下面我们来定义一个类型,处理数字:
package com.demo.interpreter;
/**
* Created by Daybreak on 2017/12/19.
*/
public class NumberExpression extends AbstractExpression {
private int number;
public NumberExpression(int num){
this.number = num;
}
@Override
public int result() {
return number;
}
}
下面因为表达式分为加减乘除,所以在抽象一个基础类,计算A、B之间的运算:
package com.demo.interpreter;
/**
* Created by Daybreak on 2017/12/19.
*/
public abstract class AbstractOperationExpression extends AbstractExpression {
protected AbstractExpression expression1;
protected AbstractExpression expression2;
public void setExpression1(AbstractExpression expression1) {
this.expression1 = expression1;
}
public void setExpression2(AbstractExpression expression2) {
this.expression2 = expression2;
}
}
下面我们简单实现数据的加和减,乘除的逻辑大同小异。
加法计算类:
package com.demo.interpreter;
/**
* Created by Daybreak on 2017/12/19.
*/
public class AddExpression extends AbstractOperationExpression {
public AddExpression(AbstractExpression expression1,AbstractExpression expression2){
setExpression1(expression1);
setExpression2(expression2);
}
@Override
public int result() {
return expression1.result() + expression1.result();
}
}
减法实现类:
package com.demo.interpreter;
/**
* Created by Daybreak on 2017/12/19.
*/
public class SubExpression extends AbstractOperationExpression {
public SubExpression(AbstractExpression expression1,AbstractExpression expression2){
setExpression1(expression1);
setExpression2(expression2);
}
@Override
public int result() {
return expression1.result() - expression2.result();
}
}
为了使用方便,我们要定义一个工具类,这个工具类就是Client调用方法,调用的时候很简单:
package com.demo.interpreter;
import java.util.Stack;
/**
* Created by Daybreak on 2017/12/19.
*/
public class OperationTools {
// 维护一个栈,用于存表达式字符串
private Stack<AbstractExpression> mAbstractExpressionStack = new Stack<>();
public int operationExpression(String expression){
AbstractExpression expression1;
AbstractExpression expression2;
// 根据具体协议来拆分字符串,规定了数字与运算符必须以空格隔开,
// 正常情况下可以用正则表达式来区分
String[] expressions = expression.split(" ");
// 循环遍历计算
for(int i = 0; i < expressions.length; i++){
String exp = expressions[i].trim();
switch (exp){
case "+":
// 取出栈顶元素当表达式1
expression1 = mAbstractExpressionStack.pop();
// 同时取出下一个字符,当表达式2
expression2 = new NumberExpression(Integer.valueOf(expressions[++i].trim()));
// 把结果当做表示式的第一位,从而继续运算
mAbstractExpressionStack.push(new AddExpression(expression1,expression2));
break;
case "-":
// 取出栈顶元素当表达式1
expression1 = mAbstractExpressionStack.pop();
// 同时取出下一个字符,当表达式2
expression2 = new NumberExpression(Integer.valueOf(expressions[++i].trim()));
// 把结果当做表示式的第一位,从而继续运算
mAbstractExpressionStack.push(new SubExpression(expression1,expression2));
break;
default:
// 不是运算符,那就是数字
mAbstractExpressionStack.push(new NumberExpression(Integer.valueOf(expressions[i].trim())));
break;
}
}
// 循环结束,返回结果。
return mAbstractExpressionStack.pop().result();
}
}
下面是使用过程,Client类为Activity:
package com.demo.interpreter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.example.daybreak.helloword.R;
public class InterpreterActivity extends AppCompatActivity {
private EditText mExpressions = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_interpreter);
mExpressions = (EditText) findViewById(R.id.expressions);
findViewById(R.id.result).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// demo先不考虑容错,并规定了数字与运算符必须以空格隔开,正常情况下可以用正则表达式来区分
int result = new OperationTools().operationExpression(mExpressions.getText().toString());
Toast.makeText(InterpreterActivity.this,"执行结果 :" + result,Toast.LENGTH_LONG).show();
}
});
}
}
这样,程序就能够简单的计算类似于“12 + 2 - 3”这类语句了。
后记
解释器的优点就是如上的扩展性,要加运算符只需要实现AbstractOperationExpression,当然缺点就是对于每一个语句都有不同的解释器,类的数量就增多了,而且对于复杂的解释行为,难以维护其抽象类,所以这个模式对于简单的语句,使用起来还是很好的。