学生个人网站设计,沧州建设局网站,做暧在线网站,前端开发的工作内容#x1f44b;hi#xff0c;我不是一名外包公司的员工#xff0c;也不会偷吃茶水间的零食#xff0c;我的梦想是能写高端CRUD #x1f525; 2025本人正在沉淀中… 博客更新速度 #x1f44d; 欢迎点赞、收藏、关注#xff0c;跟上我的更新节奏 #x1f3b5; 当你的天空突… hi我不是一名外包公司的员工也不会偷吃茶水间的零食我的梦想是能写高端CRUD 2025本人正在沉淀中… 博客更新速度 欢迎点赞、收藏、关注跟上我的更新节奏 当你的天空突然下了大雨那是我在为你炸乌云 文章目录 一、入门什么是解释器模式为什么要解释器模式如何实现解释器模式 二、解释器模式在框架源中的运用Spring 表达式语言SpEL 三、总结解释器模式的优点解释器模式的缺点解释器模式的适用场景 一、入门
什么是解释器模式
解释器模式Interpreter Pattern是一种行为设计模式用于定义语言的语法表示并提供一个解释器来处理该语法。它通常用于需要解释和执行特定语言或表达式的场景。
为什么要解释器模式
假设一个电商平台需要实现动态的促销规则例如
规则1用户是 VIP 且 订单金额 ≥ 100 元 → 享受 20 元优惠。规则2商品类别是 “电子产品” 或 库存量 50 → 允许参加秒杀活动。
这些规则需要灵活配置并且随着业务发展可能会新增条件例如添加“用户年龄 ≤ 30 岁”等。
下面是没有用解释器模式的实现代码。
public class PromotionRuleWithoutInterpreter {public static boolean checkRule1(MapString, Object context) {boolean isVip (boolean) context.get(isVip);double orderAmount (double) context.get(orderAmount);return isVip orderAmount 100;}public static boolean checkRule2(MapString, Object context) {String category (String) context.get(productCategory);int stock (int) context.get(stock);return category.equals(electronics) || stock 50;}// 每新增一个规则都需要添加一个新方法且逻辑无法复用
}存在问题
重复代码每个规则都需要手动解析字段和逻辑。难以扩展新增规则需要修改代码违反开闭原则。维护困难如果字段名或条件逻辑变化需要修改所有相关方法。
如何实现解释器模式
解释器模式的构成
抽象表达式Abstract Expression定义解释操作的接口通常包含一个interpret()方法。终结符表达式Terminal Expression实现与语法中的终结符相关的解释操作。非终结符表达式Non-terminal Expression实现语法中的规则通常包含对其他表达式的引用。上下文Context包含解释器需要的全局信息。客户端Client构建语法树并调用解释操作。主要讲需要分析的句子或表达式转换成解释器对象描述的抽象语法树然后调用解释器的解释方法当然也可以通过环节角色间访问解释器的解释方法。
【案例】大促规则 - 改 抽象表达式Abstract ExpressionRuleExpression接口。
interface RuleExpression {boolean interpret(MapString, Object context);
}终结符表达式Terminal Expression: IsVipExpressio类判断用户是否为vipOrderAmountExpression判断订单金额是否≥指定值ProductCategoryExpression类判断商品类别是否匹配。
// 终结符表达式用户是否是VIP
class IsVipExpression implements RuleExpression {Overridepublic boolean interpret(MapString, Object context) {return (boolean) context.get(isVip);}
}// 终结符表达式订单金额是否≥指定值
class OrderAmountExpression implements RuleExpression {private double minAmount;public OrderAmountExpression(double minAmount) {this.minAmount minAmount;}Overridepublic boolean interpret(MapString, 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;}Overridepublic boolean interpret(MapString, 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;}Overridepublic boolean interpret(MapString, 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;}Overridepublic boolean interpret(MapString, Object context) {return expr1.interpret(context) || expr2.interpret(context);}
}客户端假设需要判断用户是否满足规则1VIP 且订单金额≥100元客户端是负责 构建规则表达式 并 调用解释器执行规则 的部分。
public class PromotionRuleDemo {public static void main(String[] args) {// 上下文数据模拟用户订单信息MapString, Object context new HashMap();context.put(isVip, true);context.put(orderAmount, 150.0);// 构建规则表达式isVip orderAmount 100RuleExpression rule1 new AndExpression(new IsVipExpression(),new OrderAmountExpression(100.0));// 解释并执行规则boolean canApplyDiscount rule1.interpret(context);System.out.println(是否满足促销规则1: canApplyDiscount); // 输出: true}
}改造后的好处
规则可配置化 可以将促销规则抽象为表达式对象甚至通过配置文件或数据库动态生成规则树无需修改代码。 例如将规则 (isVip orderAmount 100) || (productCategory electronics) 存储为 JSON动态解析为表达式对象。灵活组合条件 通过组合AndExpression、OrExpression可以轻松实现复杂的逻辑。易于扩展 新增条件例如“用户年龄 ≤ 30”只需添加新的终结符表达式无需改动现有代码。
二、解释器模式在框架源中的运用
Spring 表达式语言SpEL
Spring 的 SpELSpring 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 的核心流程分为两个阶段
解析阶段将字符串表达式如 “2 3 * 4”解析为 抽象语法树AST树中的每个节点对应一个表达式对象。执行阶段递归遍历 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;}Overridepublic Object getValue() {// 直接返回字面量值如 42 转换为整数return this.literalValue;}
}非终结符表达式示例OpPlus加法操作。OpPlus 组合了左、右两个操作数可能是其他表达式对象递归解释执行。
public class OpPlus extends Operator {Overridepublic 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 {// 获取变量值如 #userObject lookupVariable(String name);// 获取类型转换器、函数等TypeConverter getTypeConverter();
}客户端SpelExpressionParser:SpelExpressionParser 负责将字符串转换为 Expression 对象语法树的根节点。
public class SpelExpressionParser {// 解析字符串为 Expression 对象语法树public Expression parseExpression(String expressionString) {// 使用 Tokenizer 分词Parser 构建 ASTreturn 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}
}三、总结
解释器模式的优点
易于扩展语法规则 通过添加新的表达式类可以轻松扩展语法规则符合 开闭原则对扩展开放对修改封闭。实现简单语法解析 对于简单的语法规则解释器模式提供了一种直观的实现方式将语法规则分解为多个表达式类。解耦语法解析与执行 将语法解析逻辑封装在表达式类中与业务逻辑解耦使代码更清晰、更易维护。适合领域特定语言DSL 解释器模式非常适合实现 领域特定语言如规则引擎、查询语言等能够将复杂的业务规则抽象为表达式树。灵活性 可以通过组合不同的表达式类动态构建复杂的语法树支持运行时修改规则。
解释器模式的缺点
复杂性高 对于复杂的语法规则解释器模式会导致类的数量急剧增加每个规则都需要一个表达式类增加系统复杂性。性能问题 解释器模式通常采用递归解释执行性能较低不适合对性能要求较高的场景。难以维护 随着语法规则的增加表达式类的数量会变得庞大导致代码难以维护。不适合复杂语法 解释器模式更适合处理简单的语法规则对于复杂的语法如编程语言使用解释器模式会变得非常笨拙。学习成本高 需要开发者熟悉语法树的设计和递归解释执行的原理增加了学习和实现的难度。
解释器模式的适用场景
领域特定语言DSL 当需要实现一种简单的领域特定语言时解释器模式是一种自然的选择。例如 规则引擎如促销规则、风控规则。查询语言如 SQL 条件解析。模板引擎如动态生成邮件内容。 需要动态解析和执行规则的场景 当规则需要动态配置如从数据库或配置文件中加载并在运行时解析执行时解释器模式非常适用。语法规则相对固定且简单 如果语法规则不会频繁变化且规则数量较少解释器模式可以很好地满足需求。不适合使用编译器或解析器生成工具的场景 对于简单的语法规则使用编译器或解析器生成工具如 ANTLR可能过于复杂解释器模式提供了一种轻量级的解决方案。