一、概述
Interpreter(解释器)模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。在这里使用语言这个词似乎将Interpreter模式的应用范围限制到了一个过于狭小的范围,毕竟,我们不是自然语言或者编程语言设计者,需要注意的是,这里所讨论的语言并非指复杂的自然语言或者编程语言,而是一种语义标记,Interpreter模式负责实现这种标记的定义以及将其转换为实际功能,后面应用部分对此有进一步说明。
二、结构
Interpreter模式的结构如下:
图1、Interpreter模式的类图示意
上述类图中Context用于包含一些解释器之外的全局信息,而Client则负责构建(或被给定)表示该文法定义的语言中一个特定的句子的抽象语法树。
从上述类图可以看出,Interpreter模式实际上只是Composite模式的针对特殊应用的一个特化版本,但这并不表示Interpreter模式的提出没有意义,Interpreter模式的提出使Composite模式扩展到了更深的领域--语义转换,这一点有着一定的实际意义。
解释器模式使用类来表示每一条文法规则,通过类之间的组合来实现一定的语法规则。
三、应用
当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。
上面的说法未免有点过于formal,简单说来:
1.当我们需要一个命令解释器以解释执行用户输入的指令时可以考虑使用Interpretor模式。
2.当我们需要根据客户的输入对数据进行不同显示时可以考虑使用Interpretor模式。
<Java Design Pattern: A Tutorial>一书给出了一个根据客户输入对数据进行不同形式输出的例子,很好地体现了以上几点。实质上,更多的情况下,我们可能通过组合客户选择(多项选择)得到一个命令串,交给专门的Interpretor进行解释执行,并将处理结果回显给客户,这样,可以很好地避免客户输入错误造成的不必要的复杂性。
当存在以下情况时Interpretor模式效果最好:
1、文法简单的情况。对于复杂的文法,文法的类层次变得庞大而无法管理,此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式,这样可以节省空间而且还可能节省时间。
2、效率不是一个关键问题的情况。最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下,转换器仍可用解释器模式实现,该模式仍是有用的。
四、优缺点
解释器模式提供了一个简单的方式来执行语法,而且容易修改或者扩展语法。
在解释器中不同的规则是由不同的类来实现的,这样使得添加一个新的语法规则变得简单。
正如应用部分所说,Interpretor模式比较适用于文法简单,并且对处理的效率要求较低的情况,由于Interpretor模式使用类来标示每一条文法规则,因此,当处理复杂文法时,各规则类之间的调用及组合关系将变得难以维护,效率也将大大降低。(那对于复杂的文法结构怎么办呢?)
五、举例
以下是一个运用Interpretor模式完成罗马数字到阿拉伯数字转换的例子,其中用到了后面将讲到的Template Method模式,注意,该程序只能处理万以下的数值转换,怎么将其改成可以支持万及以上数值转换,我还没有想清楚。
#include <vector>
#include <iostream>
using namespace std;
// "Context"
class Context
{
private:
string input;
int output;
public:
Context( const string& input ) : input(input), output(0) { }
friend class Expression;
friend ostream& operator << (ostream& os, Context& context)
{
return os << context.output;
}
};
// "AbstractExpression"
class Expression
{
public :
// Template Method
void Interpret( Context& context )
{
string& input = context.input;
int& output = context.output;
if (0 == input.length()) return;
if (input.find(Nine()) == 0)
{
output += 9 * Multiplier();
input = input.substr(2, input.length() - 2);
}
else if (input.find(Four()) == 0)
{
output += 4 * Multiplier();
input = input.substr(2, input.length() - 2);
}
else if (input.find(Five()) == 0)
{
output += 5 * Multiplier();
input = input.substr(1, input.length() - 1);
}
while (input.find(One()) == 0)
{
output += Multiplier();
input = input.substr(1, input.length() - 1);
}
}
virtual const char* One() = 0;
virtual const char* Four() = 0;
virtual const char* Five() = 0;
virtual const char* Nine() = 0;
virtual int Multiplier() = 0;
};
// Thousand checks for the Roman Numeral M
// "TerminalExpression"
class ThousandExpression : public Expression
{
// Methods
const char* One() { return "M"; }
const char* Four(){ return " "; }
const char* Five(){ return " "; }
const char* Nine(){ return " "; }
int Multiplier() { return 1000; }
};
// Hundred checks C, CD, D or CM
// "TerminalExpression"
class HundredExpression : public Expression
{
// Methods
const char* One() { return "C"; }
const char* Four(){ return "CD"; }
const char* Five(){ return "D"; }
const char* Nine(){ return "CM"; }
int Multiplier() { return 100; }
};
// Ten checks for X, XL, L and XC
// "TerminalExpression"
class TenExpression : public Expression
{
// Methods
const char* One() { return "X"; }
const char* Four(){ return "XL"; }
const char* Five(){ return "L"; }
const char* Nine(){ return "XC"; }
int Multiplier() { return 10; }
};
// One checks for I, II, III, IV, V, VI, VII, VIII, IX
// "TerminalExpression"
class OneExpression : public Expression
{
// Methods
const char* One() { return "I"; }
const char* Four(){ return "IV"; }
const char* Five(){ return "V"; }
const char* Nine(){ return "IX"; }
int Multiplier() { return 1; }
};
int main()
{
string roman("MCMXXVIII");
Context context(roman);
// Build the 'parse tree'
Expression* exp[] = {
new ThousandExpression(), new HundredExpression(),
new TenExpression(), new OneExpression()};
vector<Expression*> v_exp(exp, exp + sizeof(exp) / sizeof(Expression*));
vector<Expression*>::iterator it = v_exp.begin();
for (; it != v_exp.end(); it++)
{
(*it)->Interpret(context);
delete (*it);
}
cout << roman.c_str() << "=" << context << endl;
}
参考:
1、http://www.dofactory.com/Patterns/PatternInterpreter.aspx