小侃设计模式(十九)-解释器模式

1.概述

解释器模式(Interpreter Pattern)是一种使用相对较少的模式,主要使用在编译解释等场景,例如:编译器、规则引擎解释、正则表达式解析等,这些语言又被称为领域特定语言(Domain Specific Language,DSL)。在《大话设计模式》中,对解释器的定义为:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。本文将详细分析解释器模式的原理及其使用。

2.原理及使用

2.1 原理

解释器模式UML类图如下所示:
在这里插入图片描述

类图中包含的核心角色有:抽象表达式(Abstract Expression)、终结符表达式(TerminalExpression)、非终结符表达式(NonTerminalExpression),还有的类图表达式中还有上下文(Context),也就是上述类图中的Client,负责传递数据给解释器。具体解释如下:

抽象表达式(AbstractExpression):定义解释器的接口(抽象类),内部定义了具体的解释方法,子类可实现该接口方法,实现具体的解释操作;
终结符表达式(TerminalExpression):抽象表达式的子类,实现与终结符相关的操作,每一个终结符应当都有一个具体终结表达式;
非终结符表达式(NonTerminalExpression):抽象表达式的子类,用来实现与非终结符相关的操作,每一条规则都应对应一个非终结符表达式;
上下文(Context):通常用于传递数据给各解释器,各解释器都能从此处获取数据。

2.2 案例

通过解释器模式实现四则运算,计算a+b-c的值,比如输入计算表达式形式为:a+b+c-d+e,要求表达式字母不能重复,最后求出结果。
案例类图如下:
在这里插入图片描述
上述类图中,AbstractExpression 是抽象表达式类,可以获取具体变量的值;SymbolExpression 是具体的符号运算符,其内部存储符号左右两侧对应的值;AddExpression 和SubExpression 是具体的解释器类型;VarExpression 是变量解析器,存储变量及其对应的值,根据变量获取对应的值。

编码如下:

/**
 * 抽象类表达式,通过hashMap键值对,可以获取到变量的值
 */
public abstract class Expression {
    
    

    //解释公式和数值,key就是公式(表达式),参数[a,b,c],value就是具体值
    public abstract int interpreter(HashMap<String,Integer> var);

}

/**
 * 符号运算符,每个运算符号,都只与自己左右的两个数字有关系
 * 左右两个数字有可能是一个解析结果,无论何种类型,都是Expression的子类
 */
public class SymbolExpression extends Expression {
    
    

    protected Expression left;

    protected Expression right;

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

    /**
     * symbolExpression由其子类实现
     *
     * @param var
     * @return
     */
    @Override
    public int interpreter(HashMap<String, Integer> var) {
    
    
        return 0;
    }
}

/**
 * 加法解释器
 */
public class AddExpression extends SymbolExpression {
    
    

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

    @Override
    public int interpreter(HashMap<String, Integer> var) {
    
    
        return super.left.interpreter(var) + super.right.interpreter(var);
    }
}

/**
 * 减法解释器
 */
public class SubExpression extends SymbolExpression {
    
    

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

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


/**
 * 变量解析器,存储变量及其对应的值,根据变量获取对应的值
 */
public class VarExpression extends Expression {
    
    

    private String key;

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

    @Override
    public int interpreter(HashMap<String, Integer> var) {
    
    
        return var.get(this.key);
    }
}

public class Calculator {
    
    

    private Expression expression;

    public Calculator(String expression) {
    
    
        Stack<Expression> stack = new Stack<>();
        char[] charArray = expression.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])));
                    break;
            }
        }
        this.expression = stack.pop();
    }


    public int run(HashMap<String, Integer> var) {
    
    
        return this.expression.interpreter(var);
    }

}

//测试类
public class Client {
    
    

    public static void main(String[] args) throws IOException {
    
    
        String expression = getExpStr();
        HashMap<String, Integer> var = getValues(expression);
        Calculator calculator = new Calculator(expression);
        System.out.println("运算表达式:" + expression + "=" + calculator.run(var));
    }

    public static String getExpStr() throws IOException {
    
    
        System.out.println("请输入表达式:");
        return (new BufferedReader(new InputStreamReader(System.in)).readLine());
    }

    public static HashMap<String, Integer> getValues(String expr) throws IOException {
    
    
        HashMap<String, Integer> map = new HashMap<>();
        for (char ch : expr.toCharArray()) {
    
    
            if (ch != '+' && ch != '-') {
    
    
                if (!map.containsKey(String.valueOf(ch))) {
    
    
                    System.out.println("请输入" + ch + "的值:");
                    String value = new BufferedReader(new InputStreamReader(System.in)).readLine();
                    map.put(String.valueOf(ch), Integer.valueOf(value));
                }
            }
        }
        return map;
    }
}

运行结果如下:
在这里插入图片描述

2.3 注意事项

1.解释器的使用主要在底层框架中体现,主要用于一些语法及词法解释,让程序具有更好地扩展性;
2.解释器使用的场景包括:编译器、正则表达式、运算符表达式等;
3.使用解释器在一定程度上可能造成类膨胀,同时可能采用递归调用方式,导致调试非常复杂,效率较低。

3.小结

1.解释器在日常开发中使用相对较少,主要用于底层框架中;
2.解释器在一定程度上让程序具有更好地扩展性。

4.参考文献

1.《设计模式之禅》-秦小波著
2.《大话设计模式》-程杰著
3.https://www.bilibili.com/video/BV1G4411c7N4-尚硅谷设计模式
4.https://www.runoob.com/design-pattern/observer-pattern.html

猜你喜欢

转载自blog.csdn.net/qq_33479841/article/details/128368583