Java 23种设计模式(23.行为型模式-解释器模式)

文章介绍了解释器模式,通过一个加减计算的示例,展示了如何设计和实现一个简单的解释器。解释器模式定义了一个表达式接口,通过抽象语法树解析语言中的句子。文中提到,当面对具有多种组合的运算时,解释器模式能提供灵活的解决方案,但可能不适用于复杂的文法,且执行效率可能较低。适用场景包括简单语言的解释执行和重复问题的表达。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

行为型模式- 解释器模式

代码解析

类图

在这里插入图片描述

代码

abstract class AbstractExpression {
    abstract void interpret(Context context);
}

public class TerminalExpression extends AbstractExpression{
    @Override
    void interpret(Context context) {
        System.out.println("TerminalExpression");
    }
}

public class NonterminalExpression extends AbstractExpression{
    @Override
    void interpret(Context context) {
        System.out.println("NonterminalExpression");
    }
}

public class Context {
    private String input;
    private String output;

    public String getInput() {
        return input;
    }

    public void setInput(String input) {
        this.input = input;
    }

    public String getOutput() {
        return output;
    }

    public void setOutput(String output) {
        this.output = output;
    }
}

public class Client {
    public static void main(String[] args) {
        Context context = new Context();
        ArrayList<AbstractExpression> list = new ArrayList<AbstractExpression>(2);
        AbstractExpression t = new TerminalExpression();
        AbstractExpression n = new NonterminalExpression();
        list.add(t);
        list.add(n);
        for (AbstractExpression data : list) {
            data.interpret(context);
        }
    }
}

运行结果

TerminalExpression
NonterminalExpression

1.概述

设计一个软件用来进行加减计算。我们第一想法就是使用工具类,提供对应的加法和减法的工具方法。

//用于两个整数相加
public static int add(int a,int b){
    return a + b;
}

//用于两个整数相加
public static int add(int a,int b,int c){
    return a + b + c;
}

//用于n个整数相加
public static int add(Integer ... arr) {
    int sum = 0;
    for (Integer i : arr) {
        sum += i;
    }
    return sum;
}

上面的形式比较单一、有限,如果形式变化非常多,这就不符合要求,因为加法和减法运算,两个运算符与数值可以有无限种组合方式。比如 1+2+3+4+5、1+2+3-4等等。
显然,现在需要一种翻译识别机器,能够解析由数字以及 + - 符号构成的合法的运算序列。如果把运算符和数字都看作节点的话,能够逐个节点的进行读取解析运算,这就是解释器模式的思维。

定义:

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

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

文法(语法)规则:

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

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

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

上面规则描述为 :

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

抽象语法树:

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

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

在这里插入图片描述

2.结构

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

抽象表达式(Abstract Expression)角色定义解释器的接口,约定解释器的解释操作,主要包含解释方法 interpret()
终结符表达式(Terminal Expression)角色是抽象表达式的子类,用来实现文法中与终结符相关的操作,文法中的每一个终结符都有一个具体终结表达式与之相对应。
非终结符表达式(Nonterminal Expression)角色也是抽象表达式的子类,用来实现文法中与非终结符相关的操作,文法中的每条规则都对应于一个非终结符表达式。
环境(Context)角色:通常包含各个解释器需要的数据或是公共的功能,一般用来传递被所有解释器共享的数据,后面的解释器可以从这里获取这些值。
客户端(Client):主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环境角色间接访问解释器的解释方法。

3.类图

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

在这里插入图片描述

4.代码

abstract class AbstractExpression {
     abstract int interpret(Context context);
}

public class Variable extends AbstractExpression{

    private String name;

    public Variable(String name) {
        this.name = name;
    }

    @Override
    int interpret(Context context) {
        return context.getValue(this);
    }

    @Override
    public String toString() {
        return name;
    }
}

public class Plus extends AbstractExpression{
    private AbstractExpression left;
    private AbstractExpression right;

    public Plus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    public AbstractExpression getLeft() {
        return left;
    }

    public void setLeft(AbstractExpression left) {
        this.left = left;
    }

    public AbstractExpression getRight() {
        return right;
    }

    public void setRight(AbstractExpression right) {
        this.right = right;
    }

    @Override
    int interpret(Context context) {
        return left.interpret(context)+right.interpret(context);
    }

    @Override
    public String toString() {
        return "(" + left.toString() + " + " + right.toString() + ")";
    }
}

public class Minus extends AbstractExpression{
    private AbstractExpression left;
    private AbstractExpression right;

    public Minus(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    int interpret(Context context) {
        return left.interpret(context)-right.interpret(context);
    }

    @Override
    public String toString() {
        return "(" + left.toString() + " - " + right.toString() + ")";
    }
}
public class Context {
    private Map<Variable,Integer> map = new HashMap<>();
    public void assign(Variable var,Integer value){
        map.put(var,value);
    }
    public int getValue(Variable var){
        return map.get(var);
    }
}
public class Client {
    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");

        //将变量存储到环境对象中
        context.assign(a,1);
        context.assign(b,2);
        context.assign(c,3);
        context.assign(d,4);

        AbstractExpression expression = new Minus(a,new Minus(new Minus(b,c),d));

        //解释(计算)
        int result = expression.interpret(context);

        System.out.println(expression + " = " + result);
    }
}
(a - ((b - c) - d)) = 6

5.优缺点

优点:

● 易于改变和扩展文法。
由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
● 实现文法较为容易。
在抽象语法树中每一个表达式节点类的实现方式都是相似的,这些类的代码编写都不会特别复杂。
● 增加新的解释表达式较为方便。
如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合 “开闭原则”。

缺点:

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

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

6. 使用场景

● 当语言的文法较为简单,且执行效率不是关键问题时。
● 当问题重复出现,且可以用一种简单的语言来进行表达时。
● 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值