老生常谈:解释器模式
解释器模式的目的:
如果某一类问题一再的发生,此时比较好的做法是将此类问题的各个实例表达为一个简单语言中的语句,这样可以建造一个解释器,通过解释这些语句达到解决问题的目的。
解释器模式的定义:
定义语言的文法,并且建立一个解释器来解释该语言中的句子。它属于类的行为模式。这里的语言意思是使用规定格式和语法的代码。
示意案例:
解释器这个模式在实际应用当中应用并不广泛,能想到的例子也不多,可能是我水平不够,这里我拿一个把大写数字输出为阿拉伯数字数字的需求来用解释器模式实现。例如:
1:伍千肆百参拾贰,输出为5432。
2:当用户输入的是数字字符串时,直接输入数字,例如:1000,直接输出1000。
解释器模式的结构:
模式所涉及的角色:
1:抽象表达式角色:声明一个所有的具体表达式角色都需要实现的抽象接口。这个接口主要是一个interprect方法,我们叫解释方法。
注意:本示例中的抽象表达式角色中的解释方法并非抽象方法,因为这个案例特殊,数字解释的方法过程一样。
/// 抽象表达式角色
/// </summary>
public abstract class Expression
{
/// <summary>
/// 汉字数字与阿拉伯数字数字的对应字典
/// </summary>
protected Dictionary<string, int> table = new Dictionary<string, int>(9);
public Expression()
{
table.Add("壹", 1);
table.Add("贰", 2);
table.Add("参", 3);
table.Add("肆", 4);
table.Add("伍", 5);
table.Add("陆", 6);
table.Add("柒", 7);
table.Add("捌", 8);
table.Add("玖", 9);
}
/// <summary>
/// 所有的具体表达式角色都需要实现的抽象方法
/// 这个方法为虚方法,并非抽象方法,为了代码复用
/// </summary>
/// <param name="context">环境角色</param>
public virtual void Interpret(Context context)
{
//如果要处理的字符串长度为0则返回
if (context.statement.Length == 0)
{
return ;
}
foreach (string key in table.Keys)
{
int value = table[key];
if (context.statement.EndsWith(key + GetPostifix()))
{
context.data += value * Multiplier();
context.statement = context.statement.Substring(0, context.statement.Length - this.GetLength());
break;
}
if (context.statement.EndsWith("零"))
{
context.statement = context.statement.Substring(0, context.statement.Length - 1);
break;
}
if (context.statement.Length == 0)
{
return ;
}
}
}
/// <summary>
/// 取汉字数字单位
/// 个位数为空
/// 十位数为十
/// 百位数为百
/// 千位数为千
/// </summary>
/// <returns></returns>
public abstract string GetPostifix();
/// <summary>
/// 例如:个位上数字为2,则最后为2*1
/// 例如:百位上数字为3,则表示3*10
/// </summary>
/// <returns></returns>
public abstract int Multiplier();
/// <summary>
/// 例如:个位的长度为一位
/// 例如数字三十,表示两位
/// 例如四百,表示两位
/// </summary>
/// <returns></returns>
public virtual int GetLength()
{
return this.GetPostifix().Length + 1;
}
}
2:终结符表达式角色:一个具体角色。
1>:实现了抽象表达式角色所要求的接口,主要是interpret方法。
2>:方法中的每一个终结符都有一个具体终结表达式与之相对应。
说明:如果用户输入的内容不是汉字,而是数字字符串,则直接输出,不必解释。
/// 终结符表达式角色
/// 如果能换算成数字则直接换算后返回
/// </summary>
class TerminalExpression : Expression
{
/// <summary>
/// 重写解释方法
/// </summary>
/// <param name="context">环境角色</param>
public override void Interpret(Context context)
{
int i = 0;
try
{
i = int.Parse(context.statement);
//如果是数字则说明能够直接转换
//也就是说用不到非终结表达式角色
context.statement = "";
context.data = i;
}
catch
{
//说明输入的是汉字数字,不做任何处理
}
}
public override string GetPostifix()
{
return "";
}
public override int Multiplier() { return 1; }
}
3:非终结符表达式角色:这也是一个具体角色。
1>:文法中的每一条规则R=R1R2...Rn都需要一个具体的非终结符表达式类;
2>:对每一个R1R2中的符号都持有一个静态类型为Expression的实例变量;
3>:实现解释操作,解释操作以递归的方式调用上面所提到的代表R1R2中的各个符号的实例变量。
注意:本示例中由于解释的方法都一样,为些非终结符表达中的解释方法在没有重写的情况下调用抽象表达式角色的解释方法。
/// 非终结表达式角色
/// 解释个位数
/// </summary>
public class NonterminalOneExpression : Expression
{
public override string GetPostifix()
{
return "";
}
public override int Multiplier() { return 1; }
public override int GetLength()
{
return 1;
}
}
/// <summary>
/// 非终结表达式角色
/// 解释十位数
/// </summary>
public class NonterminalTenExpression : Expression
{
public override string GetPostifix()
{
return "拾";
}
public override int Multiplier() { return 10; }
public override int GetLength()
{
return 2;
}
}
/// <summary>
/// 非终结表达式角色
/// 解释百位数
/// </summary>
public class NonterminalHundredExpression : Expression
{
public override string GetPostifix()
{
return "百";
}
public override int Multiplier() { return 100; }
public override int GetLength()
{
return 2;
}
}
/// <summary>
/// 非终结表达式角色
/// 解释千位数
/// </summary>
public class NonterminalThousandExpression : Expression
{
public override string GetPostifix()
{
return "千";
}
public override int Multiplier() { return 1000; }
public override int GetLength()
{
return 2;
}
}
4:客户端角色:
1>:建造一个抽象语法树;抽象语法树的每一个节点都代表一个语句,而在每一个节点上都可以执行解释方法。这个方法的执行就代表这个语句的解释。
2>:调用解释操作。
Context context = new Context(roman);
//构造抽象语法树
ArrayList tree = new ArrayList();
//加入终结符表达式
//如果能直接转换成数字则直接返回
tree.Add(new TerminalExpression());
//非终结符,处理个位数
tree.Add(new NonterminalOneExpression());
//非终结符,处理十位数
tree.Add(new NonterminalTenExpression());
//非终结符,处理百位数
tree.Add(new NonterminalHundredExpression());
//非终结器,处理千位数
tree.Add(new NonterminalThousandExpression());
//对抽象语法树的每个枝节进行解释操作
foreach (Expression exp in tree)
{
exp.Interpret(context);
}
Console.WriteLine("{0} = {1}", roman, context.data);
5 环境角色:提供解释器之外的一些全局信息,比如变量的真实量值。
/// 环境角色
/// </summary>
public class Context
{
/// <summary>
/// 汉字表示的数字
/// </summary>
public string statement
{
get;
set;
}
/// <summary>
/// 阿拉伯数字
/// </summary>
public int data
{
get;
set;
}
/// <summary>
/// 构造函数
/// 接受一个汉字表达式数字
/// </summary>
/// <param name="statement">汉字表达式数字</param>
public Context(string statement)
{
this.statement = statement;
}
}
解释器模式适用性:
1:系统有一个简单的语言可供解释;
2:一些重复发生的问题可以用这种简单的语言表达;
3:效率不是主要的考虑。
注:
本文引用:《Java与模式》
http://hi.baidu.com/erik168/blog/item/041c240f8efcb92e6059f33e.html