【设计模式】【行为型模式】解释器模式(Interpreter)

简介: 一、入门 什么是解释器模式? 解释器模式(Interpreter Pattern)是一种行为设计模式,用于定义语言的语法表示,并提供一个解释器来处理该语法。它通常用于需要解释和执行特定语言或表达式的场

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD

🔥 2025本人正在沉淀中... 博客更新速度++

👍 欢迎点赞、收藏、关注,跟上我的更新节奏

🎵 当你的天空突然下了大雨,那是我在为你炸乌云

一、入门

什么是解释器模式?

解释器模式(Interpreter Pattern)是一种行为设计模式,用于定义语言的语法表示,并提供一个解释器来处理该语法。它通常用于需要解释和执行特定语言或表达式的场景。

为什么要解释器模式?

假设一个电商平台需要实现动态的促销规则,例如:

  1. 规则1:用户是 VIP 且 订单金额 ≥ 100 元 → 享受 20 元优惠。
  2. 规则2:商品类别是 "电子产品" 或 库存量 > 50 → 允许参加秒杀活动。

这些规则需要灵活配置,并且随着业务发展可能会新增条件(例如添加“用户年龄 ≤ 30 岁”等)。

下面是没有用解释器模式的实现代码。

public class PromotionRuleWithoutInterpreter {
   
    public static boolean checkRule1(Map<String, Object> context) {
   
        boolean isVip = (boolean) context.get("isVip");
        double orderAmount = (double) context.get("orderAmount");
        return isVip && orderAmount >= 100;
    }

    public static boolean checkRule2(Map<String, Object> context) {
   
        String category = (String) context.get("productCategory");
        int stock = (int) context.get("stock");
        return category.equals("electronics") || stock > 50;
    }

    // 每新增一个规则,都需要添加一个新方法,且逻辑无法复用!
}

存在问题

  1. 重复代码:每个规则都需要手动解析字段和逻辑。
  2. 难以扩展:新增规则需要修改代码,违反开闭原则。
  3. 维护困难:如果字段名或条件逻辑变化,需要修改所有相关方法。

如何实现解释器模式?

解释器模式的构成

  1. 抽象表达式(Abstract Expression):定义解释操作的接口,通常包含一个interpret()方法。
  2. 终结符表达式(Terminal Expression):实现与语法中的终结符相关的解释操作。
  3. 非终结符表达式(Non-terminal Expression):实现语法中的规则,通常包含对其他表达式的引用。
  4. 上下文(Context):包含解释器需要的全局信息。
  5. 客户端(Client):构建语法树并调用解释操作。主要讲需要分析的句子或表达式转换成解释器对象描述的抽象语法树,然后调用解释器的解释方法,当然也可以通过环节角色间访问解释器的解释方法。

【案例】大促规则 - 改

image.png

抽象表达式(Abstract Expression)RuleExpression接口。

interface RuleExpression {
   
    boolean interpret(Map<String, Object> context);
}

终结符表达式(Terminal Expression): IsVipExpressio类,判断用户是否为vip;OrderAmountExpression判断订单金额是否≥指定值;ProductCategoryExpression类,判断商品类别是否匹配。

// 终结符表达式:用户是否是VIP
class IsVipExpression implements RuleExpression {
   
    @Override
    public boolean interpret(Map<String, Object> context) {
   
        return (boolean) context.get("isVip");
    }
}

// 终结符表达式:订单金额是否≥指定值
class OrderAmountExpression implements RuleExpression {
   
    private double minAmount;

    public OrderAmountExpression(double minAmount) {
   
        this.minAmount = minAmount;
    }

    @Override
    public boolean interpret(Map<String, Object> context) {
   
        double amount = (double) context.get("orderAmount");
        return amount >= minAmount;
    }
}

// 终结符表达式:商品类别是否匹配
class ProductCategoryExpression implements RuleExpression {
   
    private String category;

    public ProductCategoryExpression(String category) {
   
        this.category = category;
    }

    @Override
    public boolean interpret(Map<String, Object> context) {
   
        String actualCategory = (String) context.get("productCategory");
        return actualCategory.equals(category);
    }
}

非终结符表达式(组合条件)AndExpression类,逻辑"与"操作。OrExpression类,逻辑"或"操作。

// 非终结符表达式:逻辑"与"操作
class AndExpression implements RuleExpression {
   
    private RuleExpression expr1;
    private RuleExpression expr2;

    public AndExpression(RuleExpression expr1, RuleExpression expr2) {
   
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    @Override
    public boolean interpret(Map<String, Object> context) {
   
        return expr1.interpret(context) && expr2.interpret(context);
    }
}

// 非终结符表达式:逻辑"或"操作
class OrExpression implements RuleExpression {
   
    private RuleExpression expr1;
    private RuleExpression expr2;

    public OrExpression(RuleExpression expr1, RuleExpression expr2) {
   
        this.expr1 = expr1;
        this.expr2 = expr2;
    }

    @Override
    public boolean interpret(Map<String, Object> context) {
   
        return expr1.interpret(context) || expr2.interpret(context);
    }
}

客户端,假设需要判断用户是否满足,规则1(VIP 且订单金额≥100元),客户端是负责 构建规则表达式 并 调用解释器执行规则 的部分。

public class PromotionRuleDemo {
   
    public static void main(String[] args) {
   
        // 上下文数据(模拟用户订单信息)
        Map<String, Object> context = new HashMap<>();
        context.put("isVip", true);
        context.put("orderAmount", 150.0);

        // 构建规则表达式:isVip && orderAmount >= 100
        RuleExpression rule1 = new AndExpression(
            new IsVipExpression(),
            new OrderAmountExpression(100.0)
        );

        // 解释并执行规则
        boolean canApplyDiscount = rule1.interpret(context);
        System.out.println("是否满足促销规则1: " + canApplyDiscount); // 输出: true
    }
}

改造后的好处

  1. 规则可配置化
    可以将促销规则抽象为表达式对象,甚至通过配置文件或数据库动态生成规则树,无需修改代码。
    例如:将规则 (isVip && orderAmount >= 100) || (productCategory == "electronics") 存储为 JSON,动态解析为表达式对象。
  2. 灵活组合条件
    通过组合AndExpressionOrExpression,可以轻松实现复杂的逻辑。
  3. 易于扩展
    新增条件(例如“用户年龄 ≤ 30”)只需添加新的终结符表达式,无需改动现有代码。

二、解释器模式在框架源中的运用

Spring 表达式语言(SpEL)

Spring 的 SpEL(Spring Expression Language)允许在运行时解析字符串表达式(如 "user.name""price * quantity"),并绑定到对象属性或执行逻辑。其底层实现使用了解释器模式的思想。
下面的代码时SpEL在 @Value 注解中注入动态值

@Component
public class AppConfig {
   
    // 注入配置文件中的值
    @Value("${app.name}")
    private String appName;

    // 使用 SpEL 计算值
    @Value("#{ T(java.lang.Math).random() * 100.0 }")
    private double randomNumber;

    // 引用其他 Bean 的属性
    @Value("#{userService.defaultUser.name}")
    private String defaultUserName;
}

SpEL 的核心流程分为两个阶段

  1. 解析阶段:将字符串表达式(如 "2 + 3 * 4")解析为 抽象语法树(AST),树中的每个节点对应一个表达式对象。
  2. 执行阶段:递归遍历 AST,解释每个节点并计算结果。这一过程完美契合解释器模式的 语法树解释执行 思想。
     OpPlus
     /    \
   "2"   OpMultiply
           /    \
         "3"    "4"

下面是对源码的分析:

抽象表达式Expression 接口,所有具体表达式(如字面量、运算符、方法调用)都实现此接口。

public interface Expression {
   
    // 核心方法:解释表达式并返回结果
    Object getValue() throws EvaluationException;
    // 其他重载方法(支持上下文、目标类型等)
}

终结符表达式示例LiteralExpressionLiteralExpression 直接解析字面量(如 "100"),无需依赖其他表达式。

public class LiteralExpression implements Expression {
   
    private final String literalValue;

    public LiteralExpression(String literalValue) {
   
        this.literalValue = literalValue;
    }

    @Override
    public Object getValue() {
   
        // 直接返回字面量值(如 "42" 转换为整数)
        return this.literalValue;
    }
}

非终结符表达式示例OpPlus(加法操作)。OpPlus 组合了左、右两个操作数(可能是其他表达式对象),递归解释执行。

public class OpPlus extends Operator {
   
    @Override
    public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
   
        // 递归获取左、右操作数的值
        Object leftOperand = getLeftOperand().getValueInternal(state).getValue();
        Object rightOperand = getRightOperand().getValueInternal(state).getValue();
        // 执行加法操作
        return new TypedValue(leftOperand + rightOperand);
    }
}

上下文EvaluationContext接口,StandardEvaluationContext 是默认实现,提供变量绑定和类型支持。

public interface EvaluationContext {
   
    // 获取变量值(如 "#user")
    Object lookupVariable(String name);
    // 获取类型转换器、函数等
    TypeConverter getTypeConverter();
}

客户端SpelExpressionParser:SpelExpressionParser 负责将字符串转换为 Expression 对象(语法树的根节点)。

public class SpelExpressionParser {
   
    // 解析字符串为 Expression 对象(语法树)
    public Expression parseExpression(String expressionString) {
   
        // 使用 Tokenizer 分词,Parser 构建 AST
        return this.doParseExpression(expressionString);
    }
}

测试类

public class SpELAdditionExample {
   
    public static void main(String[] args) {
   
        // 1. 创建 SpEL 解析器
        ExpressionParser parser = new SpelExpressionParser();

        // 2. 解析加法表达式
        Expression expr = parser.parseExpression("2 + 3 * 4");

        // 3. 执行表达式并获取结果
        Integer result = expr.getValue(Integer.class);

        // 4. 输出结果
        System.out.println("计算结果: " + result); // 输出: 计算结果: 14
    }
}

三、总结

解释器模式的优点

  1. 易于扩展语法规则
    通过添加新的表达式类,可以轻松扩展语法规则,符合 开闭原则(对扩展开放,对修改封闭)。
  2. 实现简单语法解析
    对于简单的语法规则,解释器模式提供了一种直观的实现方式,将语法规则分解为多个表达式类。
  3. 解耦语法解析与执行
    将语法解析逻辑封装在表达式类中,与业务逻辑解耦,使代码更清晰、更易维护。
  4. 适合领域特定语言(DSL)
    解释器模式非常适合实现 领域特定语言(如规则引擎、查询语言等),能够将复杂的业务规则抽象为表达式树。
  5. 灵活性
    可以通过组合不同的表达式类,动态构建复杂的语法树,支持运行时修改规则。

解释器模式的缺点

  1. 复杂性高
    对于复杂的语法规则,解释器模式会导致类的数量急剧增加(每个规则都需要一个表达式类),增加系统复杂性。
  2. 性能问题
    解释器模式通常采用递归解释执行,性能较低,不适合对性能要求较高的场景。
  3. 难以维护
    随着语法规则的增加,表达式类的数量会变得庞大,导致代码难以维护。
  4. 不适合复杂语法
    解释器模式更适合处理简单的语法规则,对于复杂的语法(如编程语言),使用解释器模式会变得非常笨拙。
  5. 学习成本高
    需要开发者熟悉语法树的设计和递归解释执行的原理,增加了学习和实现的难度。

解释器模式的适用场景

  1. 领域特定语言(DSL)
    当需要实现一种简单的领域特定语言时,解释器模式是一种自然的选择。例如:
    • 规则引擎(如促销规则、风控规则)。
    • 查询语言(如 SQL 条件解析)。
    • 模板引擎(如动态生成邮件内容)。
  2. 需要动态解析和执行规则的场景
    当规则需要动态配置(如从数据库或配置文件中加载)并在运行时解析执行时,解释器模式非常适用。
  3. 语法规则相对固定且简单
    如果语法规则不会频繁变化,且规则数量较少,解释器模式可以很好地满足需求。
  4. 不适合使用编译器或解析器生成工具的场景
    对于简单的语法规则,使用编译器或解析器生成工具(如 ANTLR)可能过于复杂,解释器模式提供了一种轻量级的解决方案。
目录
相关文章
|
5月前
|
设计模式 网络协议 Java
【设计模式】【行为型模式】状态模式(State)
一、入门 什么是状态模式? 状态模式(State Pattern)是一种行为设计模式,允许对象在其内部状态改变时改变其行为,使其看起来像是改变了类。状态模式的核心思想是将对象的状态封装成独立的类,并将
223 16
|
5月前
|
设计模式 算法 前端开发
【设计模式】【行为型模式】职责链模式(Chain of Responsibility)
一、入门 什么是职责链模式? 职责链模式是一种行为设计模式,它允许你将请求沿着一条链传递,直到有对象处理它为止。每个对象都有机会处理请求,或者将其传递给链中的下一个对象。 为什么需要职责链模式? 使用
176 16
|
5月前
|
设计模式 存储 Java
【设计模式】【行为型模式】备忘录模式(Memento)
一、入门 什么是备忘录模式? 备忘录模式(Memento Pattern)是一种行为设计模式,用于在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在需要时恢复该状态。它通常用于实现撤销操作
180 8
|
5月前
|
设计模式 消息中间件 Java
【设计模式】【行为型模式】命令模式(Command)
一、入门 什么是命令模式? 命令模式是一种行为设计模式,它将请求或操作封装为对象,从而使你可以用不同的请求对客户进行参数化,并支持请求的排队、记录、撤销等操作。 命令模式的核心是将“请求”封装为独立的
193 15
|
5月前
|
设计模式 存储 JavaScript
【设计模式】【行为型模式】迭代器模式(Iterator)
一、入门 什么是迭代器模式? 迭代器模式(Iterator Pattern)是一种行为设计模式,它提供了一种顺序访问聚合对象中元素的方法,而不需要暴露其底层表示。迭代器模式将遍历逻辑从聚合对象中分离出
118 11
|
5月前
|
设计模式 XML JSON
【设计模式】【行为型模式】访问者模式(Visitor)
一、入门 什么是访问者模式? 访问者模式(Visitor Pattern)是一种行为设计模式,允许你将算法与对象结构分离。通过这种方式,可以在不改变对象结构的情况下,向对象结构中的元素添加新的操作。
160 10
|
5月前
|
设计模式 Java 程序员
【设计模式】【行为型模式】中介者模式(Mediator)
一、入门 什么是中介者模式? 中介者模式(Mediator Pattern)是一种行为设计模式,旨在减少对象之间的直接依赖,通过引入一个中介者对象来协调多个对象之间的交互。这种模式特别适用于对象间存在
120 9
|
5月前
|
设计模式 消息中间件 存储
【设计模式】【行为型模式】观察者模式(Observer)
一、入门 什么是观察者模式? 观察者模式(Observer Pattern)是一种行为设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。
276 9
|
5月前
|
设计模式 Java 数据库连接
【设计模式】【创建型模式】工厂方法模式(Factory Methods)
一、入门 什么是工厂方法模式? 工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定实例化哪个类。工厂方法模式使类的实例化延迟
143 16
|
5月前
|
设计模式 负载均衡 监控
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
139 0

热门文章

最新文章