zoukankan      html  css  js  c++  java
  • 设计模式:解释器模式

    解释器模式

    概述

    定义:

    给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。

    在解释器模式中,我们需要将待解决的问题,提取出规则,抽象为一种“语言”。比如加减法运算,规则为:由数值和+-符号组成的合法序列,“1+3-2” 就是这种语言的句子。

    解释器就是要解析出来语句的含义。但是如何描述规则呢?

    文法(语法)规则:

    文法是用于描述语言的语法结构的形式规则。

    expression ::= value | plus | minus
    plus ::= expression ‘+’ expression   
    minus ::= expression ‘-’ expression  
    value ::= integer
    

    注意: 这里的符号“::=”表示“定义为”的意思,竖线 | 表示或,左右的其中一个,引号内为字符本身,引号外为语法。

    上面规则描述为 :

    表达式可以是一个值,也可以是plus或者minus运算,而plus和minus又是由表达式结合运算符构成,值的类型为整型数。

    抽象语法树:

    在计算机科学中,抽象语法树(AbstractSyntaxTree,AST),或简称语法树(Syntax tree),是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。

    用树形来表示符合文法规则的句子。

    结构

    解释器模式包含以下主要角色。

    • 抽象表达式(Abstract Expression)角色:定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()。

    • 终结符表达式(Terminal Expression)角色:是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。

    • 非终结符表达式(Nonterminal Expression)角色:也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。

    • 环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。

    • 客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

    案例实现

    【例】设计实现加减法的软件

    image-20210413115027789

    代码如下:

    /**
     * @author WGR
     * @create 2021/4/13 -- 10:42
     */
    public abstract class AbstractExpression {
        public abstract int interpret(Context context);
    }
    
    
    /**
     * @author WGR
     * @create 2021/4/13 -- 10:47
     */
    public class Context {
    
        private Map<Variable, Integer> map = new HashMap<Variable, Integer>();
    
        public void assign(Variable var, Integer value) {
            map.put(var, value);
        }
    
        public int getValue(Variable var) {
            Integer value = map.get(var);
            return value;
        }
    }
    
    
    /**
     * @author WGR
     * @create 2021/4/13 -- 10:57
     */
    public class Variable  extends AbstractExpression {
    
        private String name;
    
        public Variable(String name) {
            this.name = name;
        }
    
        @Override
        public int interpret(Context ctx) {
            return ctx.getValue(this);
        }
    
        @Override
        public String toString() {
            return name;
        }
    }
    
    /**
     * @author WGR
     * @create 2021/4/13 -- 10:52
     */
    public class Minus extends AbstractExpression {
    
        private AbstractExpression left;
        private AbstractExpression right;
    
        public Minus(AbstractExpression left, AbstractExpression right) {
            this.left = left;
            this.right = right;
        }
    
        @Override
        public int interpret(Context context) {
            return left.interpret(context) - right.interpret(context);
        }
    
        @Override
        public String toString() {
            return "(" + left.toString() + " - " + right.toString() + ")";
        }
    }
    
    /**
     * @author WGR
     * @create 2021/4/13 -- 10:48
     */
    public class Plus  extends AbstractExpression{
    
        private AbstractExpression left;
        private AbstractExpression right;
    
        public Plus(AbstractExpression left, AbstractExpression right) {
            this.left = left;
            this.right = right;
        }
    
        @Override
        public int interpret(Context context) {
            return left.interpret(context) + right.interpret(context);
        }
    
        @Override
        public String toString() {
            return "(" + left.toString() + " + " + right.toString() + ")";
        }
    }
    
    
    /**
     * @author WGR
     * @create 2021/4/13 -- 10:58
     */
    public class Test {
    
        public static void main(String[] args) {
            Context context = new Context();
    
            Variable a = new Variable("a");
            Variable b = new Variable("b");
            Variable c = new Variable("c");
            Variable d = new Variable("d");
            Variable e = new Variable("e");
            //Value v = new Value(1);
    
            context.assign(a, 1);
            context.assign(b, 2);
            context.assign(c, 3);
            context.assign(d, 4);
            context.assign(e, 5);
    
            AbstractExpression expression = new Minus(new Plus(new Plus(new Plus(a, b), c), d), e);
    
            System.out.println(expression + "= " + expression.interpret(context));
        }
    }
    
    

    执行结果:

    image-20210413135623706

    优缺点

    1,优点:

    • 易于改变和扩展文法。

      由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。

    • 实现文法较为容易。

      在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂。

    • 增加新的解释表达式较为方便。

      如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合 "开闭原则"。

    2,缺点:

    • 对于复杂文法难以维护。

      在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护。

    • 执行效率较低。

      由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

    使用场景

    • 当语言的文法较为简单,且执行效率不是关键问题时。

    • 当问题重复出现,且可以用一种简单的语言来进行表达时。

    • 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候。

  • 相关阅读:
    Crumpet – 使用很简单的响应式前端开发框架
    太赞了!超炫的页面切换动画效果【附源码下载】
    你见过动物是怎么笑的吗?赶紧来看看【组图】
    Treed – 基于拖放 操作的,强大的树形编辑器
    Perfect Scrollbar – 完美的 jQuery 滚动条插件
    Jeet – 先进,直观,灵活的 CSS 网格系统
    舌尖上的设计!10个美味的餐馆和食品网站
    推荐15款最好的 Twitter Bootstrap 开发工具
    Web 前端开发人员和设计师必读精华文章【系列二十五】
    Seen.js – 使用 SVG 或者 Canvas 渲染 3D 场景
  • 原文地址:https://www.cnblogs.com/dalianpai/p/14652924.html
Copyright © 2011-2022 走看看