zoukankan      html  css  js  c++  java
  • 设计模式-解释器模式(26)

    定义

    解释器模式(Interpreter Pattern)是一种按照规定语法对表达式进行解析的方案,在项目中较少使用。

    英文:Given a language,define a representation for its grammer along with an interpreter that uses the representation to interpret that uses the representation to interpret sentences in the language.

     翻译:给定一门语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示方法来解释语言中的句子。

    角色

    抽象表达式(Abstract Expression)角色:该角色声明一个所有的具体表达式角色都需要实现的抽象接口,该接口主要是含有一个解释操作interpreter()操作。

    终结符表达式(Terminal Expression)角色:该角色实现了抽象表达式角色所要求的接口,文法中的每一个终结符都会有一个具体终结表达式与之相对应。

    非终结符表达式(Nonterminal Expression)角色:该角色是一个具体角色,文法中的每一条规则都对应一个非终结符表达式类。

    环境角色(Context)角色:该角色提供解释器之外的全局信息。

    客户端(Client)角色:该角色创建一个抽象语法树,调用解释操作方法。

    /**
     * 抽象表达式.
     */
    public abstract class AbstractExpression {
        //每个表达式必须有一个解析任务
        public abstract Object interpreter();
    }
    
    /**
     * 终结符表达式
     */
    public class TerminalExpression extends AbstractExpression {
        //通常终结符表达式只有一个,但是有多个对象
        @Override
        public Object interpreter() {
            return null;
        }
    }
    
    /**
     * 非终结符表达式
     */
    public class NonterminalExpression extends AbstractExpression {
        //每个非终结符表达式都会对其他表达式产生依赖
        public NonterminalExpression(AbstractExpression expression) {
        }
    
        @Override
        public Object interpreter() {
            //进行文法处理
            return null;
        }
    }
    
    /**
     * 环境角色
     */
    public class Context {
        private HashMap map = new HashMap();     //用来容纳所有表达式
    }
    
    //通常Client是一个封装类,封装的过程就是传递进来一个规范语法文件,解释器分析后产生结果并返回,避免调用者与语法解释器的耦合关系
    public class Client { Context context = new Context(); public static void main(String[] args) { for(;;){ //进行语法判断,并产生递归调用 } } }

    源码

    优点

    • 简单的语法分析工具。
    • 扩展性良好,若修改语法规则,只要修改相应的非终结符表达式即可,若扩展语法,只要增加非终结符类即可。

    缺点

    • 解释器模式会引起类膨胀。每个语法都要产生一个非终结符表达式,语法比较复杂时就可能产生大量的类文件,不易维护。
    • 采用递归调动方法。每个非终结符表达式只关心与自己有关的表达式,每个表达式想要知道最终的结果,必须一层一层地剥茧,无论是面向过程的语言还是面向对象的语言,递归都是在必要条件下才使用的,它使程序不易调试且影响效率。

    使用场景

    • 重复发生的问题可以使用解释器模式。例如:多个应用服务器,每天产生大量的日志,系统需要对日志文件进行分析处理,由于各个服务器的日志格式不同,但是数据要素是相同的,按照解释器的说法就是终结符表达式都是相同的,非终结符表达式就需要制定。
    • 一个简单语法需要解释的场景。
    /**
     * 抽象解释器
     */
    public interface ArithmeticExpression {
        int interpret(Variables variables);
    }
    
    /**
     * 算数表达式中的变量--终结符角色
     */
    public class Variable implements ArithmeticExpression {
        @Override
        public int interpret(Variables variables) {
            return variables.get(this);
        }
    }
    
    /**
     * 加算法--非终结符角色
     */
    public class Plus implements ArithmeticExpression {
        ArithmeticExpression left;    //终结符角色
        ArithmeticExpression right;     //终结符角色
    
        public Plus(ArithmeticExpression left, ArithmeticExpression right) {
            this.left = left;
            this.right = right;
        }
    
        /**
         * 操作终结符角色
         * @param variables
         * @return
         */
        @Override
        public int interpret(Variables variables) {
            //终结符角色.interpret(环境角色)-->环境角色.get(this)获取终结符角色在环境角色中注册的值
            return left.interpret(variables)+right.interpret(variables);
        }
    }
    
    /**
     * 减法--非终结符角色
     */
    public class Substract implements ArithmeticExpression{
        ArithmeticExpression left;    //终结符角色
        ArithmeticExpression right;    //终结符角色
    
        public Substract(ArithmeticExpression left, ArithmeticExpression right) {
            this.left = left;
            this.right = right;
        }
    
        @Override
        public int interpret(Variables variables) {
            //终结符角色.interpret(环境角色)-->环境角色.get(this)获取终结符角色在环境角色中注册的值
            return left.interpret(variables)-right.interpret(variables);
        }
    }
    
    /**
     * 乘算法--非终结符角色
     */
    public class Multiply implements ArithmeticExpression {
        ArithmeticExpression left;      //终结符角色
        ArithmeticExpression right;     //终结符角色
    
        public Multiply(ArithmeticExpression left, ArithmeticExpression right) {
            this.left = left;
            this.right = right;
        }
    
        @Override
        public int interpret(Variables variables) {
            //终结符角色.interpret(环境角色)-->环境角色.get(this)获取终结符角色在环境角色中注册的值
            return left.interpret(variables)*right.interpret(variables);
        }
    }
    
    /**
     * 除算法--非终结符角色
     */
    public class Division implements ArithmeticExpression {
        ArithmeticExpression left;      //终结符角色
        ArithmeticExpression right;     //终结符角色
    
        public Division(ArithmeticExpression left, ArithmeticExpression right) {
            this.left = left;
            this.right = right;
        }
    
        @Override
        public int interpret(Variables variables) {
            //终结符角色.interpret(环境角色)-->环境角色.get(this)获取终结符角色在环境角色中注册的值
            return left.interpret(variables)/right.interpret(variables);
        }
    }
    
    /**
     * 环境角色--使用Map存储各个变量的值
     */
    public class Variables {
        //key为变量类型,value为变量值.Variable为封装int的包装类
        Map<Variable,Integer> v = new HashMap<Variable,Integer>();
        //封装值,将终结符及其对应的解释封装到环境角色
        public void put(Variable variable,int value){
            v.put(variable,value);
        }
        //取值,从环境角色中取出非终结符对应的值
        public int get(Variable variable){
            return v.get(variable);
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Variables v = new Variables();    //环境角色
            //终结符
            Variable x = new Variable();
            Variable y = new Variable();
            Variable z = new Variable();
            //将终结符注册到环境中
            v.put(x,10);
            v.put(y,20);
            v.put(z,30);
    
            //计算x*(y+z/x)-x
            //非终结符接口 = new 非终结符(终结符,终结符),将两个终结符注册到非终结符中操作
            ArithmeticExpression e = new Substract(new Multiply(x,new Plus(y,new Division(z,x))),x);
            //解析非终结符接口 => 非终结符成员变量(终结符)相互操作 => 终结符.interpret(环境角色)  =>  环境角色.get(this)获取终结符代表的值,拿这些值进行操作
            int result = e.interpret(v);
            System.out.println(result);
    
        }
    }    

    源码

    个人总结

    终结符实现抽象表达式的接口,interpert(环境角色)解析自身代表的value。

    非终结符实现抽象表达式接口,interpert(环境角色)解析终结符代表的的value相互操作的结果。

  • 相关阅读:
    2-SAT 问题及图论解法
    割点、强连通分量
    欧拉路、欧拉回路
    DFS 树
    【题解】[AGC 034 F] RNG and XOR【异或卷积 FWT】
    【题解】[UOJ 62] UR #5 怎样跑得更快【莫比乌斯反演】
    【题解】LOJ #2085 / 洛谷 P1587「NOI2016」循环之美【莫比乌斯反演】
    【题解】LOJ #6052 「雅礼集训 2017 Day11」DIV【莫比乌斯反演】
    【题解】[HDU 5382] GCD?LCM【莫比乌斯反演 差分 线性筛】
    【题解】[51Nod 1847] 奇怪的数学题【min_25筛 杜教筛 莫比乌斯反演】
  • 原文地址:https://www.cnblogs.com/aeolian/p/8960742.html
Copyright © 2011-2022 走看看