Java设计模式 - 程序员古德

解释器模式是一种灵活处理复杂语言或表达式的设计模式,以智能家居系统为例,用户可用自定义语言编写控制脚本,如“室温高则开空调”,先定义这种语言的简单文法,再构建解释器将脚本转为系统可理解的指令,这样,用户无需了解底层细节,就能轻松控制家居设备,未来添加新功能时,只需扩展文法和解释器,不影响现有脚本。

定义

程序员必知!解释器模式的实战应用与案例分析 - 程序员古德

解释器模式是一种行为设计模式,它提供了一种在运行时解析和执行特定语言的方式,在解释器模式中,通常定义一个语言的文法,然后实现一个解释器来解释这个文法,这种模式主要应用在需要处理一些复杂语言或者表达式,并且希望将这些语言或表达式转换成一些中间表示形式进行处理的场景中。

举一个业务中的实际案例:假设正在开发一个智能家居系统,用户可以通过系统提供的自定义语言来编写控制家居设备的脚本,比如“如果室内温度高于25度,则打开空调”,在这个场景中,可以使用解释器模式来设计系统,如下:

  1. 定义文法:需要定义一种简单的语言文法,用来描述用户的控制需求,比如包含条件语句、执行语句等。
  2. 构建解释器:根据定义的文法构建解释器,解释器能够解析用户编写的脚本,并将其转换成系统可以理解的中间表示形式。
  3. 执行操作:系统根据解释器解析的结果执行相应的操作,比如打开空调。

通过这种方式,可以让用户通过简单的脚本语言来控制复杂的家居设备,而不需要用户了解底层的实现细节,同时,如果以后需要添加新的控制功能,只需要扩展文法和解释器,而不需要修改用户已经编写好的脚本。

代码案例

程序员必知!解释器模式的实战应用与案例分析 - 程序员古德

反例

下面是一个未使用解释器模式的反例代码,在这个例子中,尝试实现一个简单的数学表达式求值功能。Calculator类,它包含了一个硬编码的求和方法,如下代码:

public class Calculator {  
    // 硬编码的求和方法,只能处理特定的表达式  
    public int calculate(int a, int b) {  
        // 假设这里总是执行加法操作  
        return a + b;  
    }  
}

创建client调用代码:

public class Client {  
    public static void main(String[] args) {  
        // 创建计算器实例  
        Calculator calculator = new Calculator();  
          
        // 调用求和方法,并输出结果  
        int result = calculator.calculate(3, 4);  
        System.out.println("Result of 3 + 4 is: " + result);  
    }  
}

执行上面的client代码,会得到以下输出:

Result of 3 + 4 is: 7

但这个代码存在一个问题,如果想要执行不同的数学操作(比如减法、乘法、除法等),Calculator类就无法满足需求了,因为它只支持加法操作,并且这个操作是硬编码在calculate方法中的,这就是未使用解释器模式的局限性:缺乏灵活性和可扩展性

正例

下面是一个使用解释器模式的正例代码,在这个代码中实现一个简单的数学表达式求值器,它能够解析并计算包含加法、减法和乘法的表达式。先定义一个Expression接口,它是所有表达式类的抽象基类:

// 表达式接口  
public interface Expression {  
    int interpret(Context context);  
}

创建实现了Expression接口的具体表达式类,这些类将代表加法、减法和乘法操作:

// 加法表达式类  
public class AddExpression implements Expression {  
    private Expression leftOperand;  
    private Expression rightOperand;  
  
    public AddExpression(Expression leftOperand, Expression rightOperand) {  
        this.leftOperand = leftOperand;  
        this.rightOperand = rightOperand;  
    }  
  
    @Override  
    public int interpret(Context context) {  
        return leftOperand.interpret(context) + rightOperand.interpret(context);  
    }  
}  
  
// 减法表达式类  
public class SubtractExpression implements Expression {  
    private Expression leftOperand;  
    private Expression rightOperand;  
  
    public SubtractExpression(Expression leftOperand, Expression rightOperand) {  
        this.leftOperand = leftOperand;  
        this.rightOperand = rightOperand;  
    }  
  
    @Override  
    public int interpret(Context context) {  
        return leftOperand.interpret(context) - rightOperand.interpret(context);  
    }  
}  
  
// 乘法表达式类  
public class MultiplyExpression implements Expression {  
    private Expression leftOperand;  
    private Expression rightOperand;  
  
    public MultiplyExpression(Expression leftOperand, Expression rightOperand) {  
        this.leftOperand = leftOperand;  
        this.rightOperand = rightOperand;  
    }  
  
    @Override  
    public int interpret(Context context) {  
        return leftOperand.interpret(context) * rightOperand.interpret(context);  
    }  
}

定义一个Context类,它用于存储变量值,在这个简单的例子中,不需要真正的上下文,但为了符合解释器模式的结构,还是创建一个,如下代码;

// 上下文类,用于存储变量值(在这个简单例子中,实际上并未使用)  
public class Context {  
    // 可以在这里添加存储变量值的逻辑  
}

创建client代码来构建表达式并调用解释器,如下代码:

public class Client {  
    public static void main(String[] args) {  
        // 创建上下文  
        Context context = new Context();  
  
        // 创建表达式树  
        // 例如表达式:(5 + 3) * 2  
        Expression five = new TerminalExpression(5);  
        Expression three = new TerminalExpression(3);  
        Expression addExpression = new AddExpression(five, three);  
        Expression two = new TerminalExpression(2);  
        Expression multiplyExpression = new MultiplyExpression(addExpression, two);  
  
        // 解释(计算)表达式  
        int result = multiplyExpression.interpret(context);  
  
        // 输出结果  
        System.out.println("Result of (5 + 3) * 2 is: " + result);  
    }  
}  
  
// 终端表达式类,代表数字字面量  
class TerminalExpression implements Expression {  
    private int value;  
  
    public TerminalExpression(int value) {  
        this.value = value;  
    }  
  
    @Override  
    public int interpret(Context context) {  
        return value;  
    }  
}

运行上面的client代码,会得到以下输出,如下:

Result of (5 + 3) * 2 is: 16

在这个例子中,使用了解释器模式来动态地构建和计算数学表达式,通过组合不同的表达式类(AddExpression, SubtractExpression, MultiplyExpression)可以创建复杂的表达式,并且解释器能够正确地计算其结果,TerminalExpression类代表了表达式中的终端符号,即数字字面量,这种模式使得表达式的解析和计算变得非常灵活和可扩展。

核心总结

程序员必知!解释器模式的实战应用与案例分析 - 程序员古德

解释器模式在日常Java开发过程中使用的非常多,它最大的优点在于灵活性:能动态地解释和执行代码,这在处理复杂逻辑或多变需求时特别有用,如编程语言解释器或动态配置系统,此外,解释器模式还支持可扩展性。它的缺点也很明显点,因为动态解释通常比预编译代码执行得更慢,同时,实现解释器模式可能相对复杂,需要仔细设计文法和解释逻辑。

在使用解释器模式时,要首先考虑性能要求,如果性能不是关键,且需要高度灵活性和可扩展性,解释器模式是很好的选择。反之,如果性能至关重要,或者需求相对固定,那么可能需要考虑其他模式。

关注我,每天学习互联网编程技术 - 程序员古德