服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服务器之家 - 编程语言 - JAVA教程 - 解析Java的设计模式编程之解释器模式的运用

解析Java的设计模式编程之解释器模式的运用

2020-04-01 14:04卡奴达摩 JAVA教程

这篇文章主要介绍了解析Java的设计模式编程之解释器模式的运用,文中对解释器模式的优缺点及适用场景作出了分析,需要的朋友可以参考下

定义:给定一种语言,定义他的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中句子。
类型:行为类模式
类图:

解析Java的设计模式编程之解释器模式的运用

解释器模式是一个比较少用的模式,本人之前也没有用过这个模式。下面我们就来一起看一下解释器模式。
 
解释器模式的结构
抽象解释器:声明一个所有具体表达式都要实现的抽象接口(或者抽象类),接口中主要是一个interpret()方法,称为解释操作。具体解释任务由它的各个实现类来完成,具体的解释器分别由终结符解释器TerminalExpression和非终结符解释器NonterminalExpression完成。
终结符表达式:实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。终结符一半是文法中的运算单元,比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。                               
非终结符表达式:文法中的每条规则对应于一个非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,+就是非终结符,解析+的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
环境角色:这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。

例子
来举一个加减乘除的例子吧,实现思路来自于《java与模式》中的例子。每个角色的功能按照上面提到的规范来实现。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
//上下文(环境)角色,使用HashMap来存储变量对应的数值
 
class Context
 
{
 
    private Map valueMap = new HashMap();
 
    public void addValue(Variable x , int y)
 
    {
 
       Integer yi = new Integer(y);
 
       valueMap.put(x , yi);
 
    }
 
    public int LookupValue(Variable x)
 
    {
 
       int i = ((Integer)valueMap.get(x)).intValue();
 
       return i ;
 
    }
 
}
 
//抽象表达式角色,也可以用接口来实现
 
abstract class Expression
 
{
 
    public abstract int interpret(Context con);
 
}
 
 
//终结符表达式角色
 
class Constant extends Expression
 
{
 
    private int i ;
 
    public Constant(int i)
 
    {
 
       this.i = i;
 
    }
 
    public int interpret(Context con)
 
    {
 
       return i ;
 
    }
 
}
 
 
class Variable extends Expression
 
{
 
    public int interpret(Context con)
 
    {
 
       //this为调用interpret方法的Variable对象
 
       return con.LookupValue(this);
 
    }
 
}
 
 
//非终结符表达式角色
 
class Add extends Expression
 
{
 
    private Expression left ,right ;
 
    public Add(Expression left , Expression right)
 
    {
 
       this.left = left ;
 
       this.right= right ;
 
    }
 
    public int interpret(Context con)
 
    {
 
       return left.interpret(con) + right.interpret(con);
 
    }
 
}
 
 
class Subtract extends Expression
 
{
 
    private Expression left , right ;
 
    public Subtract(Expression left , Expression right)
 
    {
 
       this.left = left ;
 
       this.right= right ;
 
    }
 
    public int interpret(Context con)
 
    {
 
       return left.interpret(con) - right.interpret(con);
 
    }
 
}
 
class Multiply extends Expression
 
{
 
    private Expression left , right ;
 
    public Multiply(Expression left , Expression right)
 
    {
 
       this.left = left ;
 
       this.right= right ;
 
    }
 
    public int interpret(Context con)
 
    {
 
       return left.interpret(con) * right.interpret(con);
 
    }
 
}
 
class Division extends Expression
 
{
 
    private Expression left , right ;
 
    public Division(Expression left , Expression right)
 
    {
 
       this.left = left ;
 
       this.right= right ;
 
    }
 
    public int interpret(Context con)
 
    {
 
       try{
 
              return left.interpret(con) / right.interpret(con);
 
       }catch(ArithmeticException ae)
 
       {
 
           System.out.println("被除数为0!");
 
           return -11111;
 
       }
 
    }
 
}
 
//测试程序,计算 (a*b)/(a-b+2)
 
public class Test
 
{
 
    private static Expression ex ;
 
    private static Context con ;
 
    public static void main(String[] args)
 
    {
 
       con = new Context();
 
       //设置变量、常量
 
       Variable a = new Variable();
 
       Variable b = new Variable();
 
       Constant c = new Constant(2);
 
//为变量赋值
 
       con.addValue(a , 5);
 
       con.addValue(b , 7);
 
//运算,对句子的结构由我们自己来分析,构造
 
       ex = new Division(new Multiply(a , b), new Add(new Subtract(a , b) , c));
 
       System.out.println("运算结果为:"+ex.interpret(con));
 
    }
 
}

解释器模式的优缺点
        解释器是一个简单的语法分析工具,它最显著的优点就是扩展性,修改语法规则只需要修改相应的非终结符就可以了,若扩展语法,只需要增加非终结符类就可以了。
        但是,解释器模式会引起类的膨胀,每个语法都需要产生一个非终结符表达式,语法规则比较复杂时,就可能产生大量的类文件,为维护带来非常多的麻烦。同时,由于采用递归调用方法,每个非终结符表达式只关心与自己相关的表达式,每个表达式需要知道最终的结果,必须通过递归方式,无论是面向对象的语言还是面向过程的语言,递归都是一个不推荐的方式。由于使用了大量的循环和递归,效率是一个不容忽视的问题。特别是用于解释一个解析复杂、冗长的语法时,效率是难以忍受的。
 
解释器模式的适用场景
        在以下情况下可以使用解释器模式:
有一个简单的语法规则,比如一个sql语句,如果我们需要根据sql语句进行rm转换,就可以使用解释器模式来对语句进行解释。
一些重复发生的问题,比如加减乘除四则运算,但是公式每次都不同,有时是a+b-c*d,有时是a*b+c-d,等等等等个,公式千变万化,但是都是由加减乘除四个非终结符来连接的,这时我们就可以使用解释器模式。


注意事项
       解释器模式真的是一个比较少用的模式,因为对它的维护实在是太麻烦了,想象一下,一坨一坨的非终结符解释器,假如不是事先对文法的规则了如指掌,或者是文法特别简单,则很难读懂它的逻辑。解释器模式在实际的系统开发中使用的很少,因为他会引起效率、性能以及维护等问题。

延伸 · 阅读

精彩推荐