Java设计模式之解释器模式

1. 解释器模式

1.1 定义、优缺点、适应场景

定义:解释器模式(Interpreter Pattern),是指给定一个语言(表达式),来表示它的文法,并定义一个解释器,使用该解释器来解释语言中的句子(表达式),并得到结果。例如在编译原理中,一个算术表达式通过词法分析器形成词法单元,而后这些词法单元再通过语法分析器构建成一颗抽象语法分析树。这里的词法分析器和语法分析器都可以看做是解释器

优点

  • 易于扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来扩展文法
  • 每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言
  • 增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合开闭原则

缺点

  • 解释器模式会引起类膨胀
  • 解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低

适应场景

  • 应用可以将一个需要解释执行的语言中的句子表示为一个抽象语法树,让程序具有良好的扩展性
  • 一些重复出现的问题可以用一种简单的语言来表达
  • 一个简单语法需要解释的场景
  • 比如编译器、运算表达式计算、正则表达式、机器人等

1.2 模式的结构与实现

结构

  • 环境角色(Context): 含有解释器之外的全局信息
  • 抽象表达式(AbstractExpression): 声明一个抽象的解释方法,这个方法为抽象语法树中所有的节点所共享
  • 终结符表达式(TerminalExpression): 实现与文法中的终结符相关的解释操作
  • 非终结符表达式(NonTermialExpression): 为文法中的非终结符实现解释操作
  • Client:通过Client输入Context和TerminalExpression信息

实现

通过解释器模式来实现整数四则运算,如计算a+b-c的值,具体要求

  1. 先输入表达式的形式,比如a+b+c-d+e, 要求表达式的字母不能重复
  2. 再分别输入a ,b, c, d, e的值
  3. 最后求出结果
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Stack;

public class InterpreterTest {

    public static void main(String[] args) throws IOException {
        // 例如:expStr = a+b-c
        String expStr = getExpStr();
        // 例如:inputMap = {a=10, b=30, c=20}
        HashMap<String, Integer> inputMap = getInputMap(expStr);
        // 进行计算器表达式的构造
        Calculator calculator = new Calculator(expStr);
        // 将inputMap传入计算器表达式,进行计算
        System.out.println("运算结果:" + expStr + " = " + calculator.run(inputMap));
    }

    // 获取用户输入的字符串表达式
    public static String getExpStr() throws IOException {
        System.out.print("请输入表达式:");
        return (new BufferedReader(new InputStreamReader(System.in))).readLine();
    }

    // 获得用户输入的字符串表达式,各个变量及其值HashMap
    public static HashMap<String, Integer> getInputMap(String expStr) throws IOException {
        HashMap<String, Integer> hashMap = new HashMap<>();

        for (char ch : expStr.toCharArray()) {
            if (ch != '+' && ch != '-' && ch != ' ') {
                // hashMap右的变量则不处理
                if (!hashMap.containsKey(String.valueOf(ch))) {
                    System.out.print("请输入" + String.valueOf(ch) + "的值:");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    hashMap.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }

        return hashMap;
    }
}


// 抽象表达式-变量表达式和运算符表达式的父类
abstract class Expression {

    // 由子类进行具体的实现
    // 如果是变量表达式子类,则从HashMap通过key获取值
    // 如果是运算符表达式子类,则进行运算符操作。如果运算符左右两边还是运算符表达式,则递归调用
    public abstract int interpreter(HashMap<String, Integer> inputMap);
}


// 终结符表达式-变量表达式
class VarExpression extends Expression {

    // 变量的key
    private String key;

    public VarExpression(String key) {
        this.key = key;
    }

    // 从HashMap通过key获取值
    @Override
    public int interpreter(HashMap<String, Integer> inputMap) {
        return inputMap.get(this.key);
    }
}


// 非终结符表达式-运算符表达式
class SymbolExpression extends Expression {

    // 左右表达式,可以是值表达式,也可以是运算符表达式
    protected Expression left;
    protected Expression right;

    public SymbolExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    // 具体的实现由子类去实现。这里只提供默认实现
    @Override
    public int interpreter(HashMap<String, Integer> inputMap) {
        return 0;
    }
}


// 非终结符表达式具体实现-加法表达式
class AddExpression extends SymbolExpression {

    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }

    // 进行加法运算符操作。如果运算符左右两边是值表达式,则从HashMap获取值相加
    // 如果是运算符表达式,则递归调用
    public int interpreter(HashMap<String, Integer> inputMap) {

        return super.left.interpreter(inputMap) + super.right.interpreter(inputMap);
    }
}

// 非终结符表达式具体实现-减法表达式
class SubExpression extends SymbolExpression {

    public SubExpression(Expression left, Expression right) {
        super(left, right);
    }

    public int interpreter(HashMap<String, Integer> inputMap) {
        return super.left.interpreter(inputMap) - super.right.interpreter(inputMap);
    }
}

// 环境角色-计算器
class Calculator {

    // 计算器要进行计算的表达式
    private Expression expression;

    // 解析传入的字符串表达式,构造计算器要进行计算的表达式
    // 例如:expStr = a+b-c
    public Calculator(String expStr) {
        // 用栈来存取一个临时的计算器表达式
        Stack<Expression> stack = new Stack<>();
        // 将字符串表达式拆分成字符数组
        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();
                    // 运算符后面的空字符则直接跳过
                    while (String.valueOf(charArray[++i]) == " ") {

                    }
                    // 将运算符后面的变量,构造成值表达式,然后赋值给临时的右表达式
                    right = new VarExpression(String.valueOf(charArray[i]));
                    // 根据左右表达式构造出加法表达式,作为临时的计算器表达式传入栈中
                    stack.push(new AddExpression(left, right));
                    break;
                case '-':
                    left = stack.pop();
                    while (String.valueOf(charArray[++i]) == " ") {

                    }
                    right = new VarExpression(String.valueOf(charArray[i]));
                    stack.push(new SubExpression(left, right));
                    break;
                case ' ':
                    break;
                default:
                    // 剩余的就是变量,构造成值表达式,作为临时的计算器表达式传入栈中
                    stack.push(new VarExpression(String.valueOf(charArray[i])));
                    break;
            }
        }
        // 当遍历完整个charArray数组后,从栈中获取计算器表达式
        this.expression = stack.pop();
    }

    public int run(HashMap<String, Integer> inputMap) {

        // 将用户输入的表达式的值HashMap,传入给计算器表达式进行计算
        return this.expression.interpreter(inputMap);
    }
}

运行程序,结果如下:

请输入表达式:a + b - c
请输入a的值:10
请输入b的值:30
请输入c的值:20
运算结果:a + b - c = 20
解释器模式Interpreter Pattern)是一种行为型设计模式,它定义了一种语言文法的表示,并定义了一个解释器,用于解释语言中的句子。它将一个问题分成两个部分:一部分是语言的文法规则,另一部分是解释器,用来解释规则中的句子。解释器模式可以用于处理一些简单的语言,如数学表达式、正则表达式等。 实现方式: 1. 定义抽象表达式类(AbstractExpression),它是所有表达式类的父类,声明了抽象的解释方法。 2. 定义终结符表达式类(TerminalExpression),它实现了抽象表达式类中的解释方法,用于解释语言中的终结符。 3. 定义非终结符表达式类(NonterminalExpression),它也实现了抽象表达式类中的解释方法,用于解释语言中的非终结符。 4. 定义上下文类(Context),它包含了解释器需要的一些全局信息。 5. 客户端使用时,先创建一个上下文对象,然后将需要解释的语言句子作为参数传入解释器对象中,解释器对象将句子解释成相应的结果。 优点: 1. 可扩展性好,增加新的文法规则只需要添加相应的非终结符表达式类即可。 2. 易于实现语法分析。 缺点: 1. 对于复杂的文法规则,解释器模式的类数量可能会很大,增加程序的复杂性。 2. 执行效率较低,因为需要递归调用解释器对象。 适用场景: 1. 可以用于处理一些简单的语言,如数学表达式、正则表达式等。 2. 当语言的文法规则比较复杂时,可以使用解释器模式进行语法分析。 3. 当需要对语言进行增强时,可以使用解释器模式添加新的文法规则。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值