简说设计模式——解释器模式

一、什么是解释器模式

  解释器这个名词想必大家都不会陌生,比如编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建语法分析树,最终形成一颗抽象的语法分析树。诸如此类的例子也有很多,比如编译器、正则表达式等等。

  如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子,这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。

  就比如正则表达式,它就是解释器模型的一种应用,解释器为正则表达式定义了一个文法,如何表示一个特定的正则表达式,以及如何解释这个正则表达式。

  解释器模式(Interpreter),给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。UML结构图如下:

  其中,Context是环境角色,包含解释器之外的一些全局信息;AbstractExpression为抽象表达式,声明一个抽象的解释操作,这个接口为抽象语法树中所有的节点所共享;TerminalExression为终结符表达式,实现与文法中的终结符相关联的解释操作;NonterminalExpression为非终结符表达式,为文法中的非终结符实现解释操作,对文法中每一条规则R1、R2……Rn都需要一个具体的非终结符表达式类。

  1. Context环境角色

1 public class Context { 2  3     private String input; 4     private String output; 5      6     public String getInput() { 7         return input; 8     } 9     public void setInput(String input) {10         this.input = input;11     }12     public String getOutput() {13         return output;14     }15     public void setOutput(String output) {16         this.output = output;17     }18     19 }

  2. 抽象表达式

  抽象表达式是生成语法集合(语法树)的关键,每个语法集合完成指定语法解析任务,它是通过递归调用的方式,最终由最小的语法单元进行解析完成。

1 public abstract class AbstractExpression {2     public abstract void Interpret(Context context);3 }

  3. 终结符表达式

  通常,终结符表达式比较简单,主要处理场景元素和数据的转换。

1 public class TerminalExpression extends AbstractExpression {2 3     @Override4     public void Interpret(Context context) {5         System.out.println("终端解释器");6     }7 8 }

  4. 非终结符表达式

  每个非终结符表达式都代表了一个文法规则,并且每个文法规则都只关心自己周边的文法规则的结果,因此这就产生了每个非终结符表达式调用自己周边的非终结符表达式,然后最终、最小的文法规则就是终结符表达式。

1 public class NonterminalExpression extends AbstractExpression {2 3     @Override4     public void Interpret(Context context) {5         System.out.println("非终端解释器");6     }7 8 }

  5. Client客户端

  其中list为一个语法容器,容纳一个具体的表达式。通常Client是一个封装类,封装的结果就是传递进来一个规范语法文件,解析器分析后产生结果并返回,避免了调用者与语法分析器的耦合关系。

1 public class Client { 2      3     public static void main(String[] args) { 4         Context context = new Context(); 5         List<AbstractExpression> list = new ArrayList<>(); 6          7         list.add(new TerminalExpression()); 8         list.add(new NonterminalExpression()); 9         list.add(new TerminalExpression());10         list.add(new TerminalExpression());11         12         for (AbstractExpression abstractExpression : list) {13             abstractExpression.Interpret(context);14         }15     }16 17 }

  运行结果如下:

  

二、解释器模式的应用

  1. 何时使用

  • 当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时

  2. 方法

  • 构建语法树,定义终结符与非终结符

  3. 优点

  • 可扩展性好

  4. 缺点

  • 解释器模式会引起类膨胀

  • 解释器模式采用递归调用方法,将会导致调试非常复杂

  • 使用了大量的循环和递归,效率是一个不容忽视的问题

  5. 使用场景

  • 可以将一个需要解释执行的语言中的句子表示为一个抽象语法树

  • 一些重复出现的问题可以用一种简单的语言来表达

  • 一个简单语法需要解释的场景

  6. 应用实例

  • 编译器

  • 运算表达式计算、正则表达式

  • 机器人

  7. 注意事项

  • 尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题

三、解释器模式的实现

  我们现在通过解释器模式来实现四则运算,如计算a+b的值。UML图如下:

  1. 解析器封装类

  使用Calculator构造函数传参,并解析封装。这里根据栈的“先进后出”来安排运算的先后顺序(主要用在乘除法,这里只写了加减法比较简单)。以加法为例,Calculator构造函数接收一个表达式,然后把表达式转换为char数组,并判断运算符号,如果是'+’则进行加法运算,把左边的数(left变量)和右边的数(right变量)加起来即可。

  例如a+b-c这个表达式,根据for循环,首先被压入栈中的是a元素生成的VarExpression对象,然后判断到加号时,把a元素的对象从栈中pop出来,与右边的数组b进行相加,而b是通过当前的数组游标下移一个单元格得来的(为了防止该元素被再次遍历,通过++i的方式跳过下一遍历)。减法运算同理。

1 public class Calculator { 2      3     //定义表达式 4     private Expression expression; 5      6     //构造函数传参,并解析 7     public Calculator(String expStr) { 8         //安排运算先后顺序 9         Stack<Expression> stack = new Stack<>();10         //表达式拆分为字符数组11         char[] charArray = expStr.toCharArray();12         13         Expression left = null;14         Expression right = null;15         for(int i=0; i<charArray.length; i++) {16             switch (charArray[i]) {17             case '+':    //加法18                 left = stack.pop();19                 right = new VarExpression(String.valueOf(charArray[++i]));20                 stack.push(new AddExpression(left, right));21                 break;22             case '-':    //减法23                 left = stack.pop();24                 right = new VarExpression(String.valueOf(charArray[++i]));25                 stack.push(new SubExpression(left, right));26                 break;27             default:    //公式中的变量28                 stack.push(new VarExpression(String.valueOf(charArray[i])));29                 break;30             }31         }32         this.expression = stack.pop();33     }34     35     //计算36     public int run(HashMap<String, Integer> var) {37         return this.expression.interpreter(var);38     }39 40 }

  2. 抽象表达式类

  通过Map键值对,使键对应公式参数,如a、b、c等,值为运算时取得的具体数值。

1 public abstract class Expression {2     3     //解析公式和数值,key是公式中的参数,value是具体的数值4     public abstract int interpreter(HashMap<String, Integer> var);5 6 }

  3. 变量解析器

  通过interpreter()方法从map中取之。

1 public class VarExpression extends Expression { 2      3     private String key; 4      5     public VarExpression(String key) { 6         this.key = key; 7     } 8  9     @Override10     public int interpreter(HashMap<String, Integer> var) {11         return var.get(this.key);12     }13 14 }

  4. 抽象运算符号解析器

  这里,每个运算符合都只和自己左右两个数字有关系,但左右两个数字有可能也是一个解析的结果,无论何种类型,都是Expression类的实现类。

1 public class SymbolExpression extends Expression { 2      3     protected Expression left; 4     protected Expression right; 5  6     public SymbolExpression(Expression left, Expression right) { 7         this.left = left; 8         this.right = right; 9     }10 11     @Override12     public int interpreter(HashMap<String, Integer> var) {13         // TODO Auto-generated method stub14         return 0;15     }16     17 }

  5. 加法解析器

1 public class AddExpression extends SymbolExpression { 2  3     public AddExpression(Expression left, Expression right) { 4         super(left, right); 5     } 6      7     public int interpreter(HashMap<String, Integer> var) { 8         return super.left.interpreter(var) + super.right.interpreter(var); 9     }10 11 }

  6. 减法解析器

1 public class SubExpression extends SymbolExpression { 2      3     public SubExpression(Expression left, Expression right) { 4         super(left, right); 5     } 6      7     public int interpreter(HashMap<String, Integer> var) { 8         return super.left.interpreter(var) - super.right.interpreter(var); 9     }10 11 }

  7. Client客户端

  这里就比较简单了,通过getExpStr()方法获取表达式,再通过getValue()方法获取值的映射,最后再实例化Calculator类,通过run()方法获取最终的运算结果。

1 public class Client { 2      3     public static void main(String[] args) throws IOException { 4         String expStr = getExpStr(); 5         HashMap<String, Integer> var = getValue(expStr); 6         Calculator calculator = new Calculator(expStr); 7         System.out.println("运算结果:" + expStr + "=" + calculator.run(var)); 8     } 9     10     //获得表达式11     public static String getExpStr() throws IOException {12         System.out.print("请输入表达式:");13         return (new BufferedReader(new InputStreamReader(System.in))).readLine();14     }15     16     //获得值映射17     public static HashMap<String, Integer> getValue(String expStr) throws IOException {18         HashMap<String, Integer> map = new HashMap<>();19         20         for(char ch : expStr.toCharArray()) {21             if(ch != '+' && ch != '-' ) {22                 if(! map.containsKey(String.valueOf(ch))) {23                     System.out.print("请输入" + String.valueOf(ch) + "的值:");24                     String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();25                     map.put(String.valueOf(ch), Integer.valueOf(in));26                 }27             }28         }29         30         return map;31     }32     33 }

  运算结果如下:

  

  源码地址:https://gitee.com/adamjiangwh/GoF

(0)

相关推荐

  • 图解Java设计模式之解释器模式

    图解Java设计模式之解释器模式 四则运算问题 传统方案解决四则运算问题分析 解释器模式基本介绍 解释器模式来实现四则 解析器模式在Spring框架中的源码分析 解释器模式的注意事项和细节 四则运算问 ...

  • Sqlite3之Lemon语法解析器初探(1)

    转载请注明出处 看sqlite3源码发现用到了lemon语法解析器 ,然后发现一本好的lemon教程 然后参考里面的例子 做了一些例子. Lemon语法分析器 非常小巧 只依赖于两个文件 . 以下的代 ...

  • 行为型模式之解释器模式

    目录 定义与特点 结构与实现 模式的结构 模式的实现 应用场景 扩展:Expression 表达式树和Flee 在软件开发中,会遇到有些问题多次重复出现,而且有一定的相似性和规律性.如果将它们归纳成一 ...

  • 简说设计模式——迭代器模式

    一.什么是迭代器模式 迭代器这个词在Java中出现过,即Java中使用Iterator迭代器对集合进行遍历,但迭代器模式算是一个没落的模式,基本上没人会单独写一个迭代器,除非是产品性质的开发. 迭代器 ...

  • 简说设计模式——访问者模式

    一.什么是访问者模式 访问者模式是一个相对比较简单,但结构又稍显复杂的模式,它讲的是表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作.例如,你在 ...

  • 简说设计模式——状态模式

    一.什么是状态模式 状态这个词汇我们并不陌生,在日常生活中,不同时间就有不同的状态,早上起来精神饱满,中文想睡觉,下午又渐渐恢复,晚上可能精神更旺也可能耗费体力只想睡觉,这一天中就对应着不同的状态.或 ...

  • 简说设计模式——命令模式

    一.什么是命令模式 在说命令模式前我们先来说一个小例子.很多人都有吃夜市的经历,对于那些推小车的摊位,通常只有老板一个人,既负责制作也负责收钱,我要两串烤串多放辣,旁边的人要了三串烤面筋不要辣,过了一 ...

  • 软件设计模式修炼 -- 解释器模式

    解释器是一种不常使用的设计模式,它用于描述如何构成一个简单的语言解释器,主要应用于使用面向对象语言开发的编译器和解释器设计.当我们需要开发一个新的语言时,可以考虑使用解释器模式 模式动机 如果在系统中 ...

  • 通俗易懂设计模式解析——解释器模式

    前言 今天我们来讲解释器模式[Interpreter Pattern],如何理解这一个模式呢?一个简单的例子.中英文翻译器这个东西的作用是啥呢?将不知道的英文翻译成中文以便于理解.或者把中文翻译成英文 ...

  • 简说设计模式——中介者模式

    一.什么是中介者模式 中介这个词并不陌生,就是房屋中介的那个"中介",就是中间人的意思.比如MVC模式,C(Controller控制器)是M(Model模型)和V(View视图)的 ...

  • 设计模式-门面模式

    门面模式 参考资料 图解设计模式 大话设计模式 设计模式之禅 github我见过最好的设计模式 http://c.biancheng.net/view/1326.html 定义 也称为外观模式 外观模 ...