Java/Android 设计模式系列(23)--解释器模式

  这篇博客我们来介绍一下解释器模式(Interpreter Pattern),也是行为型设计模式之一,是一种用的比较少的设计模式,其提供了一种解释语言的语法或表达式的方式,该模式定义了一个表达式接口,通过该接口解释一个特定的上下文。在这么多的设计模式中,解释器模式在实际运用上相对来说要少很多,因为我们很少会去构造一个语言的文法。虽然你几乎用不到这个模式,但是看一看还是能受到一定的启发的。

设计模式总目录

  Java/Android 设计模式系列–目录

特点

  给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
  
该模式的使用场景相当广泛,总的概括下来大概有以下几种:

  • 专用的数据库查询语言,比如 SQL;
  • 通常用来解释通信协议的专用计算机语言;
  • 如果某个简单的语言需要解释执行而且可以将语言中的语句表示为一个抽象语法树时可以考虑使用解释器模式。

UML类图

  我们来看看解释器模式的 uml 类图:
  这里写图片描述
  
这个模式有几个角色:

  • AbstractExpression:抽象表达式

  • TerminalExpression:终结符表达式

  • NonterminalExpression:非终结符表达式

  • Context:上下文环境类

  • Client:客户类

通用代码如下:

public abstract class AbstractExpression {
    /**
     * 抽象的解析方法
     * @param context 上下文环境对象
     */
    public abstract void interpret(Context context);
}

public class TerminalExpression extends AbstractExpression{
    @Override
    public void interpret(Context context) {
        //实现文法中与终结符有关的解释操作
    }
}

public class NonterminalExpression extends AbstractExpression{
    @Override
    public void interpret(Context context) {
        //实现文法中与非终结符有关的解释操作
    }
}

public class Context {
}

public class Client {
    public static void main(String[] args) {
        //根据文法对特定句子构建抽象语法树后解释
    }
}

示例与源码

  为了说明解释器模式的实现办法,这里就以 wiki 上的算术表达式的解释为例,如表达式“m + n + p”,如果我们使用解释器模式对该表达式进行解释,那么代表数字的 m、n 和 p 三个字母我们就可以看成是终结符号,而“+”这个算术运算符号则可当作非终结符号。这个简单的文法如下:

expression ::= plus | minus | variable | number
plus       ::= expression expression '+'
minus      ::= expression expression '-'
variable   ::= 'a' | 'b' | 'c' | ... | 'z'
digit      ::= '0' | '1' | ... | '9'
number     ::= digit | digit number

定义一个包含逆波兰表达式的语言:

a b +
a b c + -
a b + c a - -

于是根据上面的语言和文法,我们可以简单写出下面的示例:
AbstractExpression,TerminalExpression,NonterminalExpression

import java.util.Map;

interface Expression {
    public int interpret(Map<String,Expression> variables);
}

class Number implements Expression {
    private int number;
    public Number(int number)       { this.number = number; }
    public int interpret(Map<String,Expression> variables)  { return number; }
}

class Plus implements Expression {
    Expression leftOperand;
    Expression rightOperand;
    public Plus(Expression left, Expression right) { 
        leftOperand = left; 
        rightOperand = right;
    }

    public int interpret(Map<String,Expression> variables)  { 
        return leftOperand.interpret(variables) + rightOperand.interpret(variables);
    }
}

class Minus implements Expression {
    Expression leftOperand;
    Expression rightOperand;
    public Minus(Expression left, Expression right) { 
        leftOperand = left; 
        rightOperand = right;
    }

    public int interpret(Map<String,Expression> variables)  { 
        return leftOperand.interpret(variables) - rightOperand.interpret(variables);
    }
}

class Variable implements Expression {
    private String name;
    public Variable(String name)       { this.name = name; }
    public int interpret(Map<String,Expression> variables)  { 
        if(null==variables.get(name)) return 0; //Either return new Number(0).
        return variables.get(name).interpret(variables); 
    }
}

Context

import java.util.Map;
import java.util.Stack;

class Evaluator {
    private Expression syntaxTree;

    public Evaluator(String expression) {
        Stack<Expression> expressionStack = new Stack<Expression>();
        for (String token : expression.split(" ")) {
            if  (token.equals("+")) {
                Expression subExpression = new Plus(expressionStack.pop(), expressionStack.pop());
                expressionStack.push( subExpression );
            }
            else if (token.equals("-")) {
                // it's necessary remove first the right operand from the stack
                Expression right = expressionStack.pop();
                // ..and after the left one
                Expression left = expressionStack.pop();
                Expression subExpression = new Minus(left, right);
                expressionStack.push( subExpression );
            }
            else                        
                expressionStack.push( new Variable(token) );
        }
        syntaxTree = expressionStack.pop();
    }

    public int calculate(Map<String,Expression> context) {
        return syntaxTree.interpret(context);
    }
}

Client

import java.util.Map;
import java.util.HashMap;

public class InterpreterExample {
    public static void main(String[] args) {
        String expression = "w x z - +";
        Evaluator sentence = new Evaluator(expression);
        Map<String,Expression> variables = new HashMap<String,Expression>();
        variables.put("w", new Number(5));
        variables.put("x", new Number(10));
        variables.put("z", new Number(42));
        int result = sentence.calculate(variables);
        System.out.println(result);
    }
}

总结

  解释器模式的优点是其灵活的扩展性,当我们想对文法规则进行扩展延伸时,只需要增加相应的非终结符解释器,并在构建抽象语法树时,使用到新增的解释器对象进行具体的解释即可,非常方便。
  解释器模式的缺点也显而易见,因为对于每一条文法都可以对应至少一个解释器,其会生成大量的类,导致后期维护困难;同时,对于过于复杂的文法,构建其抽象语法树会显得异常繁琐,甚至有可能会出现需要构建多棵抽象语法树的情况,因此,对于复杂的文法并不推荐使用解释器模式。

源码下载

  https://github.com/Jakey-jp/Design-Patterns/tree/master/InterpreterPattern

引用

https://en.wikipedia.org/wiki/Interpreter_pattern
http://www.cnblogs.com/java-my-life/archive/2012/06/19/2552617.html
http://blog.csdn.net/ylchou/article/details/7594135
http://blog.csdn.net/chenhuade85/article/details/8147003

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值