前提:领域规则模式
在特定领域内,某些变化虽然频繁,但可以抽象为某种规则。这时候,结合特定领域,将问题抽象为语法规则,从而给出该领域下的一般性解决方案。
典型模式
解析器模式:Interpreter
一:解释器模式Interpreter
(一)概念
一些应用提供了内建(Build-In)的脚本或者宏语言来让用户定义他们能够在系统中进行的操作。
Interpreter模式的目的就是使用一个解释器为用户提供一个一门定义语言的语法表示的解释器,然后通过解释器来解释语言中的句子。
Interpreter模式提供了一个实现语法解释器的框架。
(二)动机
在软件构建过程中,如果某一特定领域的问题比较复杂,类似的结构不断的重复出现,如果使用普通的编程方式来实现将面临非常频繁的变化。
在这种情况下,将特定领域的问题表达为某种语法规则下的句子,然后构建一个解析器来解释这样的句子,从而达到解决问题的目的。
(三)代码分析(加减运算)
利用操作树来实现语法规则提取
string expStr = "a+b-c+d"; //使用表达式树来表示规则
0.表达式基类
class Expression { public: virtual int interpreter(map<char, int> var)=0; //解析结果 virtual ~Expression(){} };
1.构建变量表达式(叶子结点)
//变量表达式 class VarExpression: public Expression { char key; public: VarExpression(const char& key) { this->key = key; } int interpreter(map<char, int> var) override { return var[key]; } };
2.符号表达式(树结点基类)
//符号表达式 class SymbolExpression : public Expression { // 运算符左右两个参数 protected: Expression* left; Expression* right; public: SymbolExpression( Expression* left, Expression* right): left(left),right(right){ } };
3.符号表达式子类实现(树结点),进行执行
//加法运算 class AddExpression : public SymbolExpression { public: AddExpression(Expression* left, Expression* right): SymbolExpression(left,right){ } int interpreter(map<char, int> var) override { return left->interpreter(var) + right->interpreter(var); //执行操作 } }; //减法运算 class SubExpression : public SymbolExpression { public: SubExpression(Expression* left, Expression* right): SymbolExpression(left,right){ } int interpreter(map<char, int> var) override { return left->interpreter(var) - right->interpreter(var); } };
4.解析表达式(获取的是解析的表达式,不是结果)
Expression* analyse(string expStr) { stack<Expression*> expStack; //使用栈来存储表达式 Expression* left = nullptr; Expression* right = nullptr; for(int i=0; i<expStr.size(); i++) { switch(expStr[i]) { case '+': // 加法运算 left = expStack.top(); //获取栈顶数据做左运算数 right = new VarExpression(expStr[++i]); //获取表达式下一个数做有运算数 expStack.push(new AddExpression(left, right)); //将运算结果入栈 break; case '-': // 减法运算 left = expStack.top(); right = new VarExpression(expStr[++i]); expStack.push(new SubExpression(left, right)); break; default: // 变量表达式 expStack.push(new VarExpression(expStr[i])); //不是符号,我们就进行入栈操作 } } Expression* expression = expStack.top(); //获取最后栈顶表达式返回出去就是结果 return expression; }
5.表达式树构建
int main(int argc, const char * argv[]) { string expStr = "a+b-c+d-e"; //结构类似,容易抽象为语法规则,业务频繁变化 map<char, int> var; var.insert(make_pair('a',5)); var.insert(make_pair('b',2)); var.insert(make_pair('c',1)); var.insert(make_pair('d',6)); var.insert(make_pair('e',10)); Expression* expression= analyse(expStr); int result=expression->interpreter(var); //使用解析得到的表达式,可以获取结果返回 cout<<result<<endl; release(expression); return 0; }
6.释放空间,释放顺序下至上
void release(Expression* expression){ //释放表达式树的节点内存... }
(四)模式定义
给定一个语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。
——《设计模式》GoF
(五)类图(结构)
(六)要点总结
(一)Interpreter模式的应用场合是Interpreter模式应用中的难点,只有满足“业务规则频繁变化,且类似的结构不断重复出现,并且容易抽象为语法规则的问题”才适合使用Interpreter模式。
(二)使用Interpreter模式来表示文法规则,从而可以使用面向对象技巧来方便地“扩展”文法。
(三)Interpreter模式比较适合简单的文法表示,对于复杂的文法表示,Interpreter模式会产生比较大的类层次结构,需要求助于语法分析生成器这样的标准工具。
(七)案例实现(没有上面讲解使用的案例好)
#include <iostream> using namespace std; #include "string" class Context { public: Context(int num) { m_num = num; } public: void setNum(int num) { m_num = num; } int getNum() { return m_num; } void setRes(int res) { m_res = res; } int getRes() { return m_res; } private: int m_num; int m_res; }; class Expression { public: virtual void interpreter(Context *context) = 0; }; class PlusExpression:public Expression { public: virtual void interpreter(Context *context) { int num = context->getNum(); num++; context->setNum(num); context->setRes(num); } }; class MinusExpression:public Expression { public: virtual void interpreter(Context *context) { int num = context->getNum(); num--; context->setNum(num); context->setRes(num); } }; int main(void) { Context *pcxt = new Context(100); Expression *e1 = new PlusExpression(); e1->interpreter(pcxt); cout << "PlusExpression:" << pcxt->getRes() << endl; Expression *e2 = new MinusExpression(); e2->interpreter(pcxt); cout << "MinusExpression:" << pcxt->getRes() << endl; delete e1; delete e2; system("pause"); return 0; }