设计模式之禅之设计模式-解析器模式

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 一:解析器模式定义        --->解释器模式(Interpreter Pattern)是一种按照规定语法进行解析的方案,在现在项目中使用较少        --->给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子二:解析器模式角色● AbstractExpression——抽象解释器        具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和Non-terminalExpression完成。

一:解析器模式定义
        --->解释器模式(Interpreter Pattern)是一种按照规定语法进行解析的方案,在现在项目中使用较少
        --->给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子


二:解析器模式角色




● AbstractExpression——抽象解释器
        具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和Non-terminalExpression完成。
● TerminalExpression——终结符表达式
        实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。具体到我们例子就是VarExpression类,表达式中的每个终结符都在栈中产生了一个VarExpression对象。
● NonterminalExpression——非终结符表达式
        文法中的每条规则对应于一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
● Context——环境角色
        具体到我们的例子中是采用HashMap代替。


三:解析器模式的应用

【1】解析器模式的优点
        --->解释器是一个简单语法分析工具,它最显著的优点就是扩展性,修改语法规则只要修改相应的非终结符表达式就可以了,若扩展语法,则只要增加非终结符类就可以了。

【2】解析器模式的缺点

● 解释器模式会引起类膨胀
        -->每个语法都要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来了非常多的麻烦。
● 解释器模式采用递归调用方法
        -->每个非终结符表达式只关心与自己有关的表达式,每个表达式需要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下使用的,它导致调试非常复杂。想想看,如果要排查一个语法错误,我们是不是要一个断点一个断点地调试下去,直到最小的语法单元。
● 效率问题
        -->解释器模式由于使用了大量的循环和递归,效率是一个不容忽视的问题,特别是一用于解析复杂、冗长的语法时,效率是难以忍受的。


【3】解析器模式的使用场景
● 重复发生的问题可以使用解释器模式
        例如,多个应用服务器,每天产生大量的日志,需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素是相同的,按照解释器的说法就是终结符表达式都是相同的,但是非终结符表达式就需要制定了。在这种情况下,可以通过程序来一劳永逸地解决该问题。
● 一个简单语法需要解释的场景
        为什么是简单?看看非终结表达式,文法规则越多,复杂度越高,而且类间还要进行递归调用(看看我们例子中的栈)。想想看,多个类之间的调用你需要什么样的耐心和信心去排查问题。因此,解释器模式一般用来解析比较标准的字符集,例如SQL语法分析,不过该部分逐渐被专用工具所取代。
        在某些特用的商业环境下也会采用解释器模式,我们刚刚的例子就是一个商业环境,而且现在模型运算的例子非常多,目前很多商业机构已经能够提供出大量的数据进行分析。

【4】解析器模式的注意事项
        尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题。在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式,弥补Java编译型语言的不足。我们在一个银行的分析型项目中就采用JRuby进行运算处理,避免使用解释器模式的四则运算,效率和性能各方面表现良好。

四:解析器模式的最佳实践
        解释器模式在实际的系统开发中使用得非常少,因为它会引起效率、性能以及维护等问题,一般在大中型的框架型项目能够找到它的身影,如一些数据分析工具、报表设计工具、科学计算工具等,若你确实遇到“一种特定类型的问题发生的频率足够高”的情况,准备使用解释器模式时,可以考虑一下Expression4J、MESP(Math Expression String Parser)、Jep等开源的解析工具包(这三个开源产品都可以通过百度、Google搜索到,请读者自行查询),功能都异常强大,而且非常容易使用,效率也还不错,实现大多数的数学运算完全没有问题,自己没有必要从头开始编写解释器。有人已经建立了一条康庄大道,何必再走自己的泥泞小路呢?


五:解析器模式的案例
 【1】抽象表达式类

 1 package com.yeepay.sxf.template22;
 2 
 3 import java.util.HashMap;
 4 
 5 /**
 6  * 抽象表达式类
 7  * @author sxf
 8  *
 9  */
10 public abstract class Expression {
11 
12     //解析公式和数值,其中var中的key值是公式中的参数,value值是具体的数字
13     public abstract int interpreter(HashMap<String, Integer> var);
14 }
View Code

 【2】变量解析器

 1 package com.yeepay.sxf.template22;
 2 
 3 import java.util.HashMap;
 4 
 5 /**
 6  * 变量解析器
 7  * (运算公式参数)
 8  * @author sxf
 9  *
10  */
11 public class VarExpression extends Expression{
12     //公式中参数名的字母
13     //从HashMap<String,Object>中的key
14     private String key;
15 
16     public VarExpression(String key) {
17         super();
18         this.key = key;
19     }
20 
21     @Override
22     public int interpreter(HashMap<String, Integer> var) {
23         return var.get(this.key);
24     }
25     
26 }
View Code

 【3】抽象运算符号解析器

 1 package com.yeepay.sxf.template22;
 2 /**
 3  * 抽象运算符号解析器
 4  * @author sxf
 5  *
 6  */
 7 public abstract class SymbolExpression  extends Expression{
 8     //运算符号左侧的表达式
 9     protected Expression left;
10     //运算符号右侧的表达式
11     protected Expression right;
12     
13     //所有的解析公式都应只关心自己左右两个表达式的结果
14     public SymbolExpression(Expression left,Expression right){
15         this.left=left;
16         this.right=right;
17     }
18 }
View Code

 【4】加法解析器

 1 package com.yeepay.sxf.template22;
 2 
 3 import java.util.HashMap;
 4 
 5 /**
 6  * 加法解析器
 7  * @author sxf
 8  *
 9  */
10 public class AddExpression extends SymbolExpression{
11 
12     public AddExpression(Expression left, Expression right) {
13         super(left, right);
14     }
15 
16     /**
17      * 返回运算结果
18      */
19     @Override
20     public int interpreter(HashMap<String, Integer> var) {
21         //从参数中解析出加数a
22         int a=super.left.interpreter(var);
23         //从参数中解析出家数b
24         int b=super.right.interpreter(var);
25         //返回运算结果
26          return a+b;
27     }
28 
29     
30 }
View Code

 【5】减法解析器

 1 package com.yeepay.sxf.template22;
 2 
 3 import java.util.HashMap;
 4 /**
 5  * 减法解析器
 6  * @author sxf
 7  *
 8  */
 9 public class SubExpression extends SymbolExpression{
10 
11     
12     public SubExpression(Expression left, Expression right) {
13         super(left, right);
14     }
15 
16     @Override
17     public int interpreter(HashMap<String, Integer> var) {
18         //解析出被减数
19         int left=super.left.interpreter(var);
20         //解析出减数
21         int right=super.right.interpreter(var);
22          //返回运算结果
23         return left-right;
24     }
25 
26     
27 }
View Code

 【6】解析器封装类

 1 package com.yeepay.sxf.template22;
 2 
 3 import java.util.HashMap;
 4 import java.util.Stack;
 5 
 6 /**
 7  * 解析器封装类
 8  * @author sxf
 9  *
10  */
11 public class Calculator {
12 
13     //定义表达式
14     private Expression expression;
15 
16     
17     //构造函数传参,并解析
18     public Calculator(String expStr){
19         //定义一个栈,安排运算的先后顺序
20         Stack<Expression> stack=new Stack<Expression>();
21         //表达式拆分为字符数组
22         char[] charArray=expStr.toCharArray();
23         //运算
24         Expression left=null;
25         Expression right=null;
26         for(int i=0;i<charArray.length;i++){
27             switch (charArray[i]) {
28             case '+'://加法
29                 left=stack.pop();
30                 right=new VarExpression(String.valueOf(charArray[++i]));
31                 stack.push(new AddExpression(left, right));
32                 break;
33             case '-'://减法    
34                 left=stack.pop();
35                 right=new VarExpression(String.valueOf(charArray[++i]));
36                 stack.push(new SubExpression(left, right));
37                 break;
38             default:
39                 stack.push(new VarExpression(String.valueOf(charArray[i])));
40                 break;
41             }
42         }
43         this.expression=stack.pop();
44     }
45     
46     //开始运算
47     public int run(HashMap<String, Integer> var){
48         return this.expression.interpreter(var);
49     }
50 }
View Code

【7】客户端测试

  1 package com.yeepay.sxf.template22;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.IOException;
  5 import java.io.InputStreamReader;
  6 import java.util.HashMap;
  7 import java.util.Stack;
  8 
  9 /**
 10  * 客户端测试
 11  * @author sxf
 12  *
 13  */
 14 public class Client {
 15 
 16     //运行四则运算
 17     public static void main(String[] args) {
 18     
 19         test01();
 20         
 21     }
 22     
 23     
 24     /**
 25      * 测试堆栈
 26      */
 27     public static void test02(){
 28         Stack<String> aStack=new Stack<String>();
 29         aStack.add("1");
 30         aStack.add("2");
 31         aStack.add("3");
 32         
 33         String aString=aStack.peek();
 34         System.out.println("Client.main()"+aString);
 35         
 36         //aStack.clear();
 37         boolean flag=aStack.empty();
 38         System.out.println("Client.main()"+flag);
 39         
 40         System.out.println("Client.main()"+aStack.pop());
 41         System.out.println("Client.main()"+aStack.pop());
 42         System.out.println("Client.main()"+aStack.pop());
 43         
 44     }
 45     
 46     
 47     public static void test01(){
 48         String expStr;
 49         try {
 50             expStr = getExpStr();
 51             System.out.println("Client.main()"+expStr);
 52             HashMap<String, Integer> var=getValue(expStr);
 53             Calculator cal=new Calculator(expStr);
 54             System.out.println("Client.main(运算结果为:"+expStr+"="+cal.run(var));
 55         } catch (IOException e) {
 56             // TODO Auto-generated catch block
 57             e.printStackTrace();
 58         }
 59         
 60     }
 61     
 62     //获得表达式
 63     public static String getExpStr() throws IOException{
 64         System.out.println("请输入表达式");
 65         return (new BufferedReader(new InputStreamReader(System.in))).readLine();
 66     }
 67     
 68     
 69     public static HashMap<String, Integer> getValue(String exprStr) throws IOException{
 70         HashMap<String, Integer> map=new HashMap<String, Integer>();
 71         //解析有几个参数要传递
 72         for(char ch:exprStr.toCharArray()){
 73             if(ch !='+' && ch!='-'){
 74                 //解决重复参数的问题
 75                 if(!map.containsKey(String.valueOf(ch))){
 76                     String in=(new BufferedReader(new InputStreamReader(System.in))).readLine();
 77                     map.put(String.valueOf(ch), Integer.valueOf(in));
 78                 }
 79             }
 80         }
 81         return map;
 82     } 
 83     
 84     
 85     
 86     
 87     
 88     
 89     
 90     
 91     
 92     
 93     
 94     
 95     
 96     
 97     
 98     
 99     
100     
101     
102     
103 }
View Code

 

相关文章
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
1月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
29天前
|
设计模式 安全 Java
Kotlin - 改良设计模式 - 构建者模式
Kotlin - 改良设计模式 - 构建者模式
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
40 1
|
2月前
|
设计模式 PHP 开发者
PHP中的设计模式:桥接模式的解析与应用
在软件开发的浩瀚海洋中,设计模式如同灯塔一般,为开发者们指引方向。本文将深入探讨PHP中的一种重要设计模式——桥接模式。桥接模式巧妙地将抽象与实现分离,通过封装一个抽象的接口,使得实现和抽象可以独立变化。本文将阐述桥接模式的定义、结构、优缺点及其应用场景,并通过具体的PHP示例代码展示如何在实际项目中灵活运用这一设计模式。让我们一起走进桥接模式的世界,感受它的魅力所在。
|
2月前
|
设计模式 存储 数据库连接
PHP中的设计模式:单例模式的深入解析与实践
在PHP开发中,设计模式是提高代码可维护性、扩展性和复用性的关键技术之一。本文将通过探讨单例模式,一种最常用的设计模式,来揭示其在PHP中的应用及优势。单例模式确保一个类仅有一个实例,并提供一个全局访问点。通过实际案例,我们将展示如何在PHP项目中有效实现单例模式,以及如何利用这一模式优化资源配置和管理。无论是PHP初学者还是经验丰富的开发者,都能从本文中获得有价值的见解和技巧,进而提升自己的编程实践。
|
2月前
|
设计模式 Java Kotlin
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin语法,推荐查看“简洁”系列教程。本文重点介绍了构建者模式在Kotlin中的应用与改良,包括如何使用具名可选参数简化复杂对象的创建过程,以及如何在初始化代码块中对参数进行约束和校验。
30 3
|
2月前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入解析与实践
【10月更文挑战第9天】 策略模式是一种行为设计模式,它允许在运行时选择算法的行为。在PHP开发中,通过使用策略模式,我们可以轻松切换算法或逻辑处理方式而无需修改现有代码结构。本文将深入探讨策略模式的定义、结构以及如何在PHP中实现该模式,并通过实际案例展示其应用价值和优势。
36 1
|
2月前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入解析与应用
【10月更文挑战第8天】 在软件开发的浩瀚宇宙中,设计模式如同星辰指引,照亮了代码设计与架构的航道。本文旨在深入探索PHP语境下策略模式(Strategy Pattern)的精髓,不仅剖析其内核原理,还将其融入实战演练,让理论在实践中生根发芽。策略模式,作为解决“如何优雅地封装算法族”的答案,以其独特的灵活性与扩展性,赋予PHP应用以动态变换行为的能力,而无需牵动既有的类结构。
37 2
|
2月前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的深入解析与实践
【10月更文挑战第12天】 在软件开发的世界中,设计模式是解决常见问题的最佳实践。它们不是具体的代码,而是一种编码和设计经验的总结。在PHP开发中,合理运用设计模式可以极大地提高代码的可维护性、扩展性和复用性。本文将深入探讨策略模式(Strategy Pattern)的原理、实现方式及其在PHP中的应用。通过具体示例,我们将展示如何利用策略模式来解耦算法与对象,从而让代码更加灵活和易于管理。
19 0

推荐镜像

更多