解释器模式(Interpreter Pattern)提供了评估语言的 或者表达式的方式,属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在SQL解析、符号处理引擎等。
意图
给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
解决问题
对于一些固定文法构建一个结束句子的解释器。
如何解决
构建语法树,定义终结符与非终结符。
优点
1. 扩展性比较好,灵活;
2. 增加了新的解释表达式的方式
3. 易于实现简单文法
缺点
1. 可利用场景比较少;
2. 对于复杂的文法比较难维护;
3. 解释器模式容易引起类膨胀 ;
4. 解释器模式采用递归调用方法
使用场景
1. 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
2. 些重复出现的问题可以用一种简单的语言来进行表达。
3. 一个简单语法需要解释的场景
注意事项
可利用场景比较少
实例:
游戏是这样的,网页上有一个编辑框,屏幕上有一只小狗,比如你在编辑框中输入这样的句子:down run 10;按下回车,这个时候,你就看到屏幕上的小狗向下跑动了10个方格大小的长度;你再输入up walk 5,按下回车,小狗就会向上走动5个方格大小的长度。
就如上面说的那个游戏,我输入up walk 5 ,我必须按照:移动方向+移动方式+移动距离这种格式输入指令,而这格式的指令就是一种文法,只有按照我定义的这种文法去输入,才能控制小狗移动。当然,输入up walk 5,小狗肯定是听不懂的。他听不懂怎么办呢?所以我需要一个工具去将我输入的内容翻译成小狗能听懂的东西,而这个工具就是定义中提到的解释器,解释器对我输入的指令进行解释,然后将解释得到的指令发送给屏幕上的小狗,小狗听懂了,就进行实际的移动。
下面我尽量解释文法规则和抽象语法树
在解释器模式的定义中,已到了一个词:文法。我想很有必要学习一下。拿上面的游戏这个例子进行说明,我可以定义以下五条文法:
expression ::= direction action distance | composite //表达式
composite ::= expression 'and' expression //复合表达式
direction ::= 'up' | 'down' | 'left' | 'right' //移动方向
action ::= 'move' | 'walk' //移动方式
distance ::= an integer //移动距离
上面的5条 文法规则,对应5个单位,这些单文可分为两大类:终结符(例如 direction/ action/ distance)是语言的最小组成单位,不能进行再次拆分 ;非终结符(expression / composite)它们都是一个完整的句子,包含一系列终结符或非终结符。
我们就是根据上面定义的一些文法可以构成更多复杂的语句,计算机程序将根据这些语句进行某种操作;而我们这里列出的文法,计算机是无法直接看懂的,所以,我们需要对我们定义的文法进行解释;就好比,我们编写的C++代码,计算机是看不懂的,我们需要进行编译一样。解释器模式,就提供一种模式去给计算机解释我们定义的文法,让计算机根据我们的文法去进行工作。
在文法规则定义中可以使用一些符号来表示不同的含义,如使用“|”表示或,使用“{”和“}”表示组合,使用“*”表示出现0次或多次等,其中使用频率最高的符号是表示“或”关系的“|”,如文法规则“bool Value ::= 0 | 1”表示终结符表达式bool Value的取值可以为0或者1。
除了使用文法规则来定义一个语言,在解释器模式中还可以通过一种称之为抽象语法树的图形方式来直观地表示语言的构成,每一棵语法树对应一个语言实例,对于上面的游戏文法规则,可以通过以下的抽象语法树来进行表示:
UML类图
AbstractExpression :明一个抽闲的解释操作,这个接口被抽象语法树中所有的节点所共享
TerminalExpression:一个句子中的每个终结符需要该类的一个实例,他实现与文法中的终结符相关联的解释操作;
NonterminalExpression :
- 对于文法中的每一条规则都需要一个NonternimalExpression类;
- 为文法中的的每个符号都维护一个AbstractExpression类型的实例变量;
- 为文法中的非终结符实现解释操作,在实现时,一般要递归地调用表示文法符号的那些对象的解释操作;
Client : 构建一个需要进行解释操作的文法句子,然后调用解释操作进行解释;
实际进行解释的过程:
1. Client 构建一个句子,它是NonterminalExpression 和 TerminalExpression的实例的一个抽象语法树,然后始化上下问进行调用解释操作;
2. 每一个非终结符表达式节点定义相应子表达式的解释操作。而各终结符表达式的解释操作构成了递归的基础;
3. 每一个节点的解释操作作用上下文来存储和访问解释器的状态。
code :
//解释器模式
/*
游戏是这样的,网页上有一个编辑框,屏幕上有一只小狗,比如你在编辑框中输入这样的句子:down run 10;按下回车,这个时候,你就看到屏幕上的小狗向下跑动了10个方格大小的长度;你再输入up walk 5,按下回车,小狗就会向上走动5个方格大小的长度。
这里用代码来实现上面的游戏,只不过不是控制小狗在屏幕上移动了,而是将对应的控制指令翻译成汉语进行表示,这和翻译成控制小狗移动的指令的原理是一样的。比如现在有指令:down run 10;那么,经过解释器模式得到的结果为:向下跑动10。
*/
#define MAX_SIZE 256
#define SAFE_DELETE(p) if(p) {delete p ; p =NULL;}
const wchar_t *const DOWN = L"down"; //字符串前面添加L代表宽字符串
const wchar_t *const UP = L"up";
const wchar_t *const LEFT = L"left";
const wchar_t *const RIGHT = L"right";
const wchar_t *const MOVE = L"move";
const wchar_t *const WALK = L"walk";
class AbstractNode
{
public:
virtual wchar_t *Interpret() = 0;
};
class AnNode : public AbstractNode
{
public:
AnNode(AbstractNode* _left, AbstractNode* _right)
:left(_left)
, right(_right)
{}
wchar_t *Interpret()//解释
{
wchar_t *pResult = new wchar_t[MAX_SIZE];
memset(pResult , 0 ,MAX_SIZE * sizeof(wchar_t));
wchar_t *pLeft = left->Interpret();
wchar_t *pRight = right->Interpret();
wcscat_s(pResult, MAX_SIZE, pLeft); //wsccat_s()函数的功能和strcat一样
wcscat_s(pResult, MAX_SIZE, pRight);
SAFE_DELETE (pLeft);
SAFE_DELETE(pRight);
return pResult;
}
private:
AbstractNode *left;
AbstractNode *right;
};
class SentenceNode : public AbstractNode
{
public:
SentenceNode(AbstractNode*direction, AbstractNode* action, AbstractNode* distance)
:m_pDirection(direction)
, m_pAction(action)
, m_pDistance(distance)
{}
wchar_t *Interpret()//解释
{
wchar_t *pResult = new wchar_t[MAX_SIZE];
memset(pResult , 0 , MAX_SIZE * sizeof(wchar_t));
wchar_t *pDirection = m_pDirection->Interpret();
wchar_t *pAction = m_pAction->Interpret();
wchar_t *pDistance = m_pDistance->Interpret();
wcscat_s(pResult, MAX_SIZE, pDirection);
wcscat_s(pResult, MAX_SIZE, pDistance);
wcscat_s(pResult, MAX_SIZE, pAction);
SAFE_DELETE (pDistance);
SAFE_DELETE(pDirection);
SAFE_DELETE( pAction);
return pResult;
}
private:
AbstractNode* m_pDirection;
AbstractNode* m_pAction;
AbstractNode* m_pDistance;
};
class DirectionNode : public AbstractNode
{
public:
DirectionNode(wchar_t *direction)
:m_pDirection(direction)
{}
wchar_t *Interpret()//解释
{
wchar_t *pResult = new wchar_t[MAX_SIZE];
memset(pResult , 0 , MAX_SIZE * sizeof(wchar_t));
if (! _wcsicmp(m_pDirection, DOWN)) //比较字符串忽律大小写
{
wcscat_s(pResult, MAX_SIZE, L"向下");
}
else if (!_wcsicmp(m_pDirection, UP))
{
wcscat_s(pResult , MAX_SIZE , L"向上");
}
else if (!_wcsicmp(pResult, LEFT))
{
wcscat_s(pResult, MAX_SIZE, L"向左");
}
else if (!_wcsicmp(pResult, RIGHT))
{
wcscat_s(pResult, MAX_SIZE, L"向右");
}
else
wcscat_s(pResult , MAX_SIZE , L"无效指令");
SAFE_DELETE(m_pDirection);
return pResult;
}
private:
wchar_t *m_pDirection;
};
class ActionNode : public AbstractNode
{
public:
ActionNode(wchar_t *action )
:m_pAction(action)
{}
wchar_t *Interpret()
{
wchar_t *pResult = new wchar_t[MAX_SIZE];
memset(pResult, 0, MAX_SIZE * sizeof(wchar_t));
if (!_wcsicmp(m_pAction, MOVE))
wcscat_s(pResult, MAX_SIZE, L"移动");
else if (!_wcsicmp(pResult, WALK))
wcscat_s(pResult, MAX_SIZE, L"走动");
else
wcscat_s(pResult, MAX_SIZE, L"无效指令");
SAFE_DELETE(m_pAction);
return pResult;
}
private:
wchar_t *m_pAction;
};
class DistanceNode : public AbstractNode
{
public:
DistanceNode(wchar_t * distance )
:m_pDistance(distance)
{}
wchar_t *Interpret()
{
wchar_t *pResult = new wchar_t[MAX_SIZE];
memset(pResult, 0 , MAX_SIZE * sizeof(wchar_t) );
wcscat_s(pResult, MAX_SIZE, m_pDistance);
SAFE_DELETE(m_pDistance);
return pResult;
}
private:
wchar_t *m_pDistance;
};
class InstructionHandler
{
public:
InstructionHandler(wchar_t *instruction)
:m_pInstruction(instruction)
{}
void Handler();
void Output();
private:
void SplitInstruction(wchar_t **&instruction, int &size);
wchar_t *m_pInstruction;
AbstractNode* m_pTree;
};
void InstructionHandler::Handler()
{
AbstractNode*pLeft = NULL;
AbstractNode*pRight = NULL;
AbstractNode*pDirection = NULL;
AbstractNode*pDistance = NULL;
AbstractNode*pAction = NULL;
vector<AbstractNode* > node;//存储指令表达式
wchar_t **InstructionArray = NULL;
int size;
SplitInstruction(InstructionArray, size);
for (int i = 0; i < size; ++i )
{
if (!_wcsicmp(InstructionArray[i], L"and"))//指令由两个表达式组成
{
wchar_t *pDirectionStr = InstructionArray[++i];
pDirection = new DirectionNode(pDirectionStr);
wchar_t *pActionStr = InstructionArray[++i];
pAction = new ActionNode(pActionStr);
wchar_t *pDistanceStr = InstructionArray[++i];
pDistance = new DistanceNode(pDistanceStr);
}
else
{
wchar_t *pDirectionStr = InstructionArray[i];
pDirection = new DirectionNode(pDirectionStr);
wchar_t *pActionStr = InstructionArray[++i];
pAction = new ActionNode(pActionStr);
wchar_t *pDistanceStr = InstructionArray[++i];
pDistance = new DistanceNode(pDistanceStr);
pLeft = new SentenceNode(pDirection, pAction, pDistance);
node.push_back(pLeft);
}
}
m_pTree = node[node.size() - 1];
}
void InstructionHandler::Output()
{
wchar_t *pResult = m_pTree->Interpret();
setlocale(LC_ALL, "");
wprintf_s(L"%s\n", pResult);
SAFE_DELETE(pResult);
}
void InstructionHandler::SplitInstruction(wchar_t **&instruction, int &size)
{
instruction = new wchar_t*[10];
memset(instruction, 0, 10 * sizeof(wchar_t*));
for (int i = 0; i < 10; ++i)
{
instruction[i] = new wchar_t[10];
memset(instruction[i], 0, 10 * sizeof(wchar_t));
}
size = 0;
int n = 0;
while (*m_pInstruction != L'\0')
{
if (*m_pInstruction == L' ')
{
size++;
m_pInstruction++;
n = 0;
continue;
}
instruction[size][n++] = *m_pInstruction++;
}
size++;
}
int test_Interpret()//解释器模式
{
wchar_t *pInstructionStr = L"up move 5 and down walk 10";
InstructionHandler *pInstructionHandler = new InstructionHandler(pInstructionStr);
pInstructionHandler->Handler();
pInstructionHandler->Output();
SAFE_DELETE(pInstructionHandler);
system("pause");
return 0;
}
总结
解释模式在实际的系统开发中使用的非常少,因为他会引起效率、性能以及维护方面的问题,且难度较大。
所以,我们更多的是理解解释器模式就Ok了。
如果上面的例子code,很难理解,那么可以看看这两篇博客:
http://www.cnblogs.com/lang5230/archive/2016/04/09/5370497.html
http://blog.csdn.net/lcl_data/article/details/9259905