"领域规则"模式
使用场景
在特定领域中,某些变化虽然频繁,但可以抽象为某种规则。这时候,结合特定领域,将问题抽象为语法规则,从而给出在该领域
下的一般性解决方案。
典型模式
Interpreter(解释器模式)
Interpreter 解释器模式
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
意图:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
主要解决:对于一些固定文法构建一个解释句子的解释器。
何时使用:如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
如何解决:构建语法树,定义终结符与非终结符。
关键代码:构建环境类,包含解释器之外的一些全局信息,一般是 HashMap。
应用实例:编译器、运算表达式计算。
优点: 1、可扩展性比较好,灵活。 2、增加了新的解释表达式的方式。 3、易于实现简单文法。
缺点: 1、可利用场景比较少。 2、对于复杂的文法比较难维护。 3、解释器模式会引起类膨胀。 4、解释器模式采用递归调用方法。
使用场景: 1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。 2、一些重复出现的问题可以用一种简单的语言来进行表达。 3、一个简单语法需要解释的场景。
注意事项:可利用场景比较少,JAVA 中如果碰到可以用 expression4J 代替。
使用场景
在软件构建过程中,如果某一特定领域的问题比较复杂,类似的结构不断重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。
在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,从而达到解决问题的目的。
模式定义
给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。
类图结构
代码举例
简单的加减程序
MainActivity.java
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
public class MainActivity extends AppCompatActivity {
private Button mExecuteButton;
private TextView mResultText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mExecuteButton = (Button) findViewById(R.id.execute_button);
mResultText = (TextView) findViewById(R.id.result_text_view);
mExecuteButton.setOnClickListener(listener);
}
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
//有了明确的规则 a,b,c,d,e 是操作数 + - 是操作符 a+b 之后 又变成新的操作数
String expStr = "a+b-c+d-e";
Map<String, Integer> var = new HashMap<String, Integer>();
var.put("a", 5);
var.put("b", 2);
var.put("c", 1);
var.put("d", 6);
var.put("e", 8);
//analyse()将字符串表达式 解析成 表达式树
Expression expression = Expression.analyse(expStr);
//解析 表达式树
int result = expression.interpreter(var);
mResultText.setText(expStr+ " = " + result);
}
};
}
Expression.java
import java.util.Map;
import java.util.Stack;
//表达式的基类 使用表达式树 构成 语法规则
public abstract class Expression {
public abstract int interpreter(Map<String, Integer> var);
public static Expression analyse(String expStr) {
Stack<Expression> expStack = new Stack<>();
Expression left;
Expression right;
for (int i = 0; i < expStr.length(); i++) {
switch (expStr.charAt(i)) {
case '+':
//加法运算
left = expStack.pop(); //【1】 a 【7】 a + b - c
right = new VarExpression(expStr.charAt(++i)); //【2】 b 【8】 d
expStack.push(new AddExpression(left, right)); //【3】 a + b 【9】 a+b-c + d
break;
case '-':
//减法运算
left = expStack.pop(); // 【4】a + b
right = new VarExpression(expStr.charAt(++i)); // 【5】c
expStack.push(new SubExpression(left, right)); //【6】 a + b - c
break;
default:
//变量表达式
expStack.push(new VarExpression(expStr.charAt(i)));
}
}
//最终 栈顶 弹出的元素是 a+b-c+d
Expression expression = expStack.pop();
return expression;
}
}
VarExpression.java
import java.util.Map;
//变量表达式 a b c d e 都是变量表达式
public class VarExpression extends Expression{
char key;
public VarExpression(char key) {
this.key = key;
}
@Override
public int interpreter(Map<String, Integer> var) {
//变量表达式解决比较简单 取值即可
return var.get(String.valueOf(key));
}
}
SymbolExpression.java
//符号表达式 + - * /
public abstract class SymbolExpression extends Expression{
//运算符左右两个参数
protected Expression left;
protected Expression right;
public SymbolExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
}
AddExpression.java
import java.util.Map;
// + 表达式
public class AddExpression extends SymbolExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> var) {
//解析的时候 取左边操作数 + 右边操作数
return left.interpreter(var) + right.interpreter(var);
}
}
SubExpression.java
import java.util.Map;
// - 表达式
public class SubExpression extends SymbolExpression {
public SubExpression(Expression left, Expression right) {
super(left, right);
}
@Override
public int interpreter(Map<String, Integer> var) {
return left.interpreter(var) - right.interpreter(var);
}
}
表达式的语法树
类图结构中 Context 对应代码中的 MainActivity.java 中的 Map var。它就是上下文,传入相关参数。
AbstractExpression 对应代码中的 Expression。TerminalExpression 终端表达式,到此为止不能往下执行了,对应代码中的操作符a,b,c,d,即VarExpression类。NonterminalExpression 非终端表达式,即SymbolExpression(符号表达式), AddExpression 和 SubExpression。
要点总结
Interpreter模式的应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的结构不断重复出现,并且容易抽象为语法规则的问题”才适合使用Interpreter模式。
使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧来方便地“扩展”文法。
Interpreter模式比较适合简单的文法表示,对于复杂的文法表示, Interperter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具。