解释器模式简介
如果在项目开发过程中,一种特定类型的问题发生的频率特别高,那么就可以将该问题的各个实例表述为一个简单语言中的句子,这样就可以通过设计一个解释器,此解释器可以通过解释该句子,来解决此问题。
解释器模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。
解释器模式:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
解释器模式结构
解释器模式的结构:
- AbstractExpression(抽象表达式):声明了抽象的解释操作interpret(),是所有终结符表达式和非终结符表达式的基类;
- TerminalExpression(终结符表达式):终结符是文法规则的组成元素中最基本的语言单位,不能再分解。终结符表达式实现了与文法规则中终结符相关的解释操作,句子中的每一个终结符都是该类的一个实例。
- NonterminalExpression(非终结符表达式):实现了文法规则中非终结符的解释操作,因为非终结符表达式同样可以包含终结符表达式,所以终结符表达式可以是非终结符表达式的成员。
- Context(环境类):即上下文类,用于存储解释器之外的一些全局信息,通常临时存储需要解释的语句。
抽象表达式声明了抽象接口interpret(),终结符表达式和非终结符表达式式具体实现了该接口。其中,终结符表达式的interpret()接口实现了具体的解释操作,而非终结符表达式中可能包含终结符表达式或者非终结符表达式,所以非终结符表达式的interpret()接口中可能是递归调用每一个组成部分的interpret()方法。
解释器模式代码实例
设计一个简单的解释器,使得系统可以解释0和1的或运算和与运算(不考虑或运算和与运算的优先级,即从左往右依次运算),语句表达式和输出结果的几个实例如下表:
表达式 | 输出结果 |
---|---|
1 and 1 | 1 |
1 or 1 | 1 |
1 or 0 | 1 |
1 and 0 | 0 |
0 and 0 | 0 |
0 or 0 | 0 |
1 and 1 or 0 | 1 |
0 or 1 and 0 | 0 |
0 or 1 and 1 or 1 | 1 |
1 or 0 and 1 and 0 or 0 | 0 |
结合前面叙述的解释器模式的结构和本例,可以划分出以下角色:
- 终结符表达式角色——值节点(ValueNode):0、1,因为它们是表达式的基本组成元素,不可再细分
- 终结符表达式角色——运算符节点(OperatorNode):运算符号“and”和“or” ,同样也是表达式的基本组成元素
- 非终结符表达式角色——句子节点(SentenceNode):类似于“1 and 1”这样的表达式或者更长的组合表达式
- 上下文类角色——处理者(Handler):保存输入的表达式和输出的结果
InterpreterPattern.h
#include <vector>
#include <string.h>
#include <iostream>
using namespace std;
// 抽象表达式类
class AbstractNode
{
public:
AbstractNode() {
}
virtual ~AbstractNode() {
}
// 声明抽象接口
virtual char interpret() = 0;
};
// 终结符表达式:ValueNode
class ValueNode : public AbstractNode
{
public:
ValueNode() {
}
ValueNode(int iValue)
{
this->value = iValue;
}
// 实现解释操作
char interpret()
{
return value;
}
private:
int value;
};
// 终结符表达式:OperationNode
class OperatorNode : public AbstractNode
{
public:
OperatorNode() {
}
OperatorNode(string iOp)
{
this->op = iOp;
}
// 实现解释操作
char interpret()
{
if (op == "and")
{
return '&';
}
else if (op == "or")
{
return '|';
}
return 0;
}
private:
string op;
};
// 非终结符表达式:SentenceNode
class SentenceNode : public AbstractNode
{
public:
SentenceNode() {
}
SentenceNode(AbstractNode *iLeftNode,
AbstractNode *iRightNode, AbstractNode *iOperatorNode)
{
this->leftNode = iLeftNode;
this->rightNode = iRightNode;
this->operatorNode = iOperatorNode;
}
char interpret()
{
if (operatorNode->interpret() == '&')
{
return leftNode->interpret() & rightNode->interpret();
}
else
{
return leftNode->interpret() | rightNode->interpret();
}
return 0;
}
private:
AbstractNode *leftNode;
AbstractNode *rightNode;
AbstractNode *operatorNode;
};
// 处理者
class Handler
{
public:
Handler() {
}
void setInput(string iInput)
{
this->input = iInput;
}
void handle()
{
AbstractNode *left = NULL;
AbstractNode *right = NULL;
AbstractNode *op = NULL;
AbstractNode *sentence = NULL;
string iInput = this->input;
vector<string> inputList;
char *inputCh = const_cast<char *>(iInput.c_str());
char *token = strtok(inputCh, " ");
while (token != NULL)
{
inputList.push_back(token);
token = strtok(NULL, " ");
}
for (int i = 0; i < inputList.size() - 2; i += 2)
{
left = new ValueNode(*(inputList[i].c_str()));
op = new OperatorNode(inputList[i + 1]);
right = new ValueNode(*(inputList[i + 2].c_str()));
sentence = new SentenceNode(left, right, op);
inputList[i + 2] = string(1, sentence->interpret());
}
string tmpRes = inputList[inputList.size() - 1];
if (tmpRes == "1")
{
result = 1;
}
else if (tmpRes == "0")
{
result = 0;
}
else
{
result = -1;
}
this->output();
}
void output()
{
cout << input.c_str() << " = " << std::to_string(result) << endl;
}
private:
string input;
char result;
};
InterpreterPattern.cpp
#include <iostream>
#include "InterpreterPattern.h"
int main()
{
Handler *handler = new Handler();
string input_1 = "1 and 1";
string input_2 = "1 and 0";
string input_3 = "0 and 1";
string input_4 = "0 and 0";
string input_5 = "0 or 0";
string input_6 = "0 or 1";
string input_7 = "1 or 0";
string input_8 = "1 or 1";
string input_9 = "1 and 0 or 1";
string input_10 = "0 or 0 and 1";
string input_11 = "1 or 1 and 1 and 0";
string input_12 = "0 and 1 and 1 and 1";
string input_13 = "0 and 1 and 1 and 1 or 1 or 0 and 1";
handler->setInput(input_1);
handler->handle();
handler->setInput(input_2);
handler->handle();
handler->setInput(input_3);
handler->handle();
handler->setInput(input_4);
handler->handle();
handler->setInput(input_5);
handler->handle();
handler->setInput(input_6);
handler->handle();
handler->setInput(input_7);
handler->handle();
handler->setInput(input_8);
handler->handle();
handler->setInput(input_9);
handler->handle();
handler->setInput(input_10);
handler->handle();
handler->setInput(input_11);
handler->handle();
handler->setInput(input_12);
handler->handle();
handler->setInput(input_13);
handler->handle();
return 0;
}
解释器模式总结
优点:
- 易于改变和扩展文法,在解释器中使用类表示语言的文法规则,可以通过继承等机制类改变或扩展文法;
- 每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言;
- 如果要增加新的解释表达式,只需增加一个新的终结符表达式或非终结符表达式类,无需修改原有代码,符合开闭原则。
缺点:
- 对于复杂文法难以维护。在解释器模式中每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会大量增加,导致系统难以管理和维护;
- 执行效率低,因为解释器模式中有大量循环和递归调用。
适用环境:
- 一些重复出现的问题可以用一种简单的语言进行表达;
- 一个语言的文法较为简单;
- 不考虑执行效率的问题时可以使用解释器模式。