设计模式 - interpreter

interpreter模式在GOF中的定义是:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

定义看起来是非常晦涩。简单来讲,解释器模式通常用于处理特定格式的问题表示,通过定义解释器,能够解释该问题问题表示。例如我们工作过程中遇到的公式计算,公式可以看做是一种特定的问题表示,它具有通用性,正确的公式就应该被正确解析。如公式a+b+c,解释器模式就是要能够处理形如此种的文法,且能够提供相当的扩展性,如支持*,/功能等。

解释器在项目中使用较少,它多用于框架和工具类的开发,类图如下:

  • AbstractExpression 抽象解释器

      具体的解释任务由各个实现类完成,具体的解释器分别由TerminalExpression和NonterminalExpression完成。

  • TerminalExpression终结符表达式

      实现与文法中的元素相关联的解释操作,通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。具体到我们例子就是VarExpression类,表达式中的每个终结符都在堆栈中产生了一个VarExpression对象。

  • NonterminalExpression 非终结符表达式

      文法中的每条规则对应于一个非终结表达式,具体到我们的例子就是加减法规则分别对应到AddExpression和SubExpression两个类。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。

  • Context 环境角色

      具体到我们的例子中是采用HashMap代替

优点:

解释器模式的最突出的优点在于其扩展性,修改语法时只要修改相应的解释器就行了,扩展也非常容易,新增解释器即可。

缺点:

解释器会引起类膨胀,如果语法不断扩展,我们必须新增解释器,这样无可避免的导致类的膨胀;另外,解释器模式使用递归计算,这必然导致效率低下。

最佳实践:

解释器模式在实际使用中非常少,因其会导致性能和维护问题,一般它只会出现在数据分析报表设计等使用场景。对于平时的应用时,可以考虑已有的Expression4J、MESP(Math Expression String Parser)、Jep等开源的解析工具包,避免自己开发。

下面给出一个通过解释器实现四则运算的例子(http://www.cnblogs.com/cbf4life/archive/2009/12/17/1626125.html)

抽象表达式类:提供统一的接口

1
2
3
4
5
6
7
8
package com.inspur.jiyq.designpattern.interpreter;
 
import java.util.Map;
 
public abstract class Expression {
    //解析公式和数值,其中var中的key值是是公式中的参数,value值是具体的数字
    public abstract int interpreter(Map<String, Integer> var);
}

  抽象符号表达式类:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.inspur.jiyq.designpattern.interpreter;
 
public abstract class SymbolExpression extends Expression{
    protected Expression left;
    protected Expression right;
     
    //所有解析公式应该只关心自己左右两个表达式的结果
    public SymbolExpression(Expression left, Expression right)
    {
        this.left = left;
        this.right = right;
    }
}

  加法解析器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.inspur.jiyq.designpattern.interpreter;
 
import java.util.Map;
 
public class AddExpression extends SymbolExpression {
 
    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }
 
    @Override
    public int interpreter(Map<String, Integer> var) {
        return super.left.interpreter(var) + super.right.interpreter(var);
    }
 
}

  减法解析器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.inspur.jiyq.designpattern.interpreter;
 
import java.util.Map;
 
public class SubExpression extends SymbolExpression {
 
    public SubExpression(Expression left, Expression right) {
        super(left, right);
    }
 
    @Override
    public int interpreter(Map<String, Integer> var) {
        return super.left.interpreter(var) - super.right.interpreter(var);
    }
 
}

  变量解析器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.inspur.jiyq.designpattern.interpreter;
 
import java.util.Map;
 
public class VarExpression extends Expression{
    private String key;
     
    public VarExpression(String key)
    {
        this.key = key;
    }
 
    @Override
    public int interpreter(Map<String, Integer> var) {
        return var.get(this.key);
    }
}

  解析器封装类:(实现运算逻辑)

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
package com.inspur.jiyq.designpattern.interpreter;
 
import java.util.Map;
import java.util.Stack;
 
public class Calculator {
    // 定义的表达式
    private Expression expression;
 
    // 构造函数传参并解析
    public Calculator(String expStr) {
        // 定义一个堆栈,安排运算的先后顺序
        Stack<Expression> stack = new Stack<Expression>();
 
        // 表达式拆分为字符数组
        char[] charArray = expStr.toCharArray();
 
        // 运算
        Expression left = null;
        Expression right = null;
 
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
            case '+':// 加法
                // 加法结果放到堆栈中
                left = stack.pop();
                right = new VarExpression(String.valueOf(charArray[++i]));
                stack.push(new AddExpression(left, right));
                break;
            case '-'://公式中的变量
                left = stack.pop();
                right = new VarExpression(String.valueOf(charArray[++i]));
                stack.push(new SubExpression(left, right));
                break;
            default:
                stack.push(new VarExpression(String.valueOf(charArray[i])));
            }
        }
        this.expression = stack.pop();
    }
     
    //开始运算
    public int run(Map<String, Integer> var)
    {
        return this.expression.interpreter(var);
    }
}

  驱动测试类:

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
package com.inspur.jiyq.designpattern.interpreter;
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
 
public class Client {
    // 运行四则运算
    public static void main(String[] args) throws IOException {
        String expStr = getExpStr();
 
        // 赋值
        Map<String, Integer> var = getValue(expStr);
        Calculator cal = new Calculator(expStr);
        System.out.println("运算结果为:" + expStr + "=" + cal.run(var));
    }
 
    public static String getExpStr() throws IOException {
        System.out.println("请输入表达式:");
 
        return (new BufferedReader(new InputStreamReader(System.in)))
                .readLine();
    }
 
    // 获得值映射
    public static Map<String, Integer> getValue(String exprStr)
            throws IOException {
        Map<String, Integer> map = new HashMap<String, Integer>();
        // 解析有几个参数要传递
        for (char ch : exprStr.toCharArray()) {
            if (ch != '+' && ch != '-') {
 
                // 解决重复参数的问题
                if (!map.containsKey(String.valueOf(ch))) {
                    System.out.print("请输入" + ch + "的值:");
 
                    String in = (new BufferedReader(new InputStreamReader(
                            System.in))).readLine();
 
                    map.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }
        return map;
    }
}

  

 

posted @   纪玉奇  阅读(378)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Linux系统下SQL Server数据库镜像配置全流程详解
· 现代计算机视觉入门之:什么是视频
阅读排行:
· Sdcb Chats 技术博客:数据库 ID 选型的曲折之路 - 从 Guid 到自增 ID,再到
· .NET Core GC压缩(compact_phase)底层原理浅谈
· Winform-耗时操作导致界面渲染滞后
· Phi小模型开发教程:C#使用本地模型Phi视觉模型分析图像,实现图片分类、搜索等功能
· 语音处理 开源项目 EchoSharp
点击右上角即可分享
微信分享提示