行为型模式之解释器模式


在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性。如果将它们归纳成一种简单的语言,那么这些问题实例将是该语言的一些句子,这样就可以用“编译原理”中的解释器模式来实现了。

虽然使用解释器模式的实例不是很多,但对于满足以上特点,且对运行效率要求不是很高的应用实例,如果用解释器模式来实现,其效果是非常好的。

解释器模式的定义

解释器(Interpreter)模式的定义:是指给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该 “表示” 来解释语言中的句子。是一种按照规定的语法(文法)进行解析的模式,属于行为型模式。

就比如编译器可以将源码编译解释为机器码,让cpu能进行识别并运行。解释器模式的作用其实与编译器一样,都是将一些固定的文法(即语法)进行解释,构建出一个解释句子的解释器。简单理解,解释器是一个简单语法分析工具,它可以识别句子语义,分离终结符号和非终结符号,提取出需要的信息,让我们能针对不同的信息作出相应的处理。其核心思想是识别文法,构建解释。

解释器模式的结构

解释器模式包含以下主要角色:
1.抽象表达式(AbstractExpression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。

2.终结符表达式(TerminalExpression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。比如:公式R=R1+R2,R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符(R1,R2)。

3.非终结符表达式(NonterminalExpression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2,解析“+”的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度增加而增加,原则上没法文法规则都对应一个非终结符表达式。

4.环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。比如:R=R1+R2,给R1赋值100,给R2赋值200,这些信息就存放到环境中。

解释器模式的实现

用解释器模式设计一个丹尼斯购物识别vip打折程序。
说明:假如是丹尼斯超市的vip购物就可以打八折,其他超市或者非vip不打折。

public class InterpreterPatternDemo {
	 public static void main(String[] args){
	        Context c=new Context();
	        c.freeRide("丹尼斯的vip用户");
	        c.freeRide("万达的vip用户");
	        c.freeRide("胖东来的vip用户");
	        c.freeRide("丹尼斯的非vip用户");
	    }
	}

	//抽象表达式类
	interface Expression{
	    public boolean interpret(String info);//解释方法
	}
	
	//终结符表达式类
	class TerminalExpression implements Expression{
		
	    private Set<String> set= new HashSet<String>();
	    
	    public TerminalExpression(String[] data){
	        for(int i=0;i<data.length;i++) {
	        	set.add(data[i]);
	        }  	
	    }
	    public boolean interpret(String info){
	        if(set.contains(info)){
	            return true;
	        }
	        return false;
	    }
	}
	//非终结符表达式类
	class AndExpression implements Expression{
	    private Expression supermarket=null;    
	    private Expression shopper=null;
	    
	    public AndExpression(Expression supermarket, Expression shopper) {
	    	this.supermarket = supermarket;
			this.shopper = shopper;
		}

		public boolean interpret(String info){
	        String s[]=info.split("的");       
	        return supermarket.interpret(s[0])&&shopper.interpret(s[1]);
	    }
	}
	
	//环境类
	class Context{
	    private String[] supermarkets={"丹尼斯"};
	    private String[] shoppers={"vip用户"};
	    private Expression expression;
	    public Context(){
	        Expression supermarket=new TerminalExpression(supermarkets);
	        Expression shopper=new TerminalExpression(shoppers);
	        expression=new AndExpression(supermarket,shopper);
	    }
	    public void freeRide(String info){
	        boolean ok = expression.interpret(info);
	        if(ok) {
	        	System.out.println("您是"+info+",您本次购物打八折!");
	        }else {
	        	System.out.println(info+",本次购物不打折!");   
	        }
	    }
}

程序运行结果如下:

您是丹尼斯的vip用户,您本次购物打八折!
万达的vip用户,本次购物不打折!
胖东来的vip用户,本次购物不打折!
丹尼斯的非vip用户,本次购物不打折!

解释器模式的应用场景

我们程序中,如果存在一种特定类型的问题,该类型问题涉及多个不同的实例,但是具备固定文法描述,那么可以使用解释器模式对该类型问题进行解释,分离出需要的信息,根据获取的信息作出相应的处理.简而言之,对于一些固定文法构建一个解释句子的解释器。适用于以下场景:
1.当语言的文法较为简单,且执行效率不是关键问题时。
2.当问题重复出现,且可以用一种简单的语言来进行表达时。
3.当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。

注意:解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。

解释器模式的优缺点

优点:
1.扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
2.容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

缺点:
1.执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
2.会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
3.可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。

<<上一篇:备忘录模式

设计模式篇到此告一段落。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值