找回密码
 立即注册
首页 业界区 业界 掌握设计模式--解释器模式

掌握设计模式--解释器模式

姘轻拎 2025-6-3 10:44:40
解释器模式(Interpreter Pattern)

解释器模式(Interpreter Pattern)是一种行为型设计模式,用于定义一种语言的文法表示,并提供一个解释器来解释该语言中的句子。这种模式通常用于开发需要解析、解释和执行特定语言或表达式的应用程序。
主要目的是为特定类型的问题定义一种语言,然后用该语言的解释器来解决问题。
主要组成部分

解释器模式的结构通常包括以下几个部分:

  • 抽象表达式(AbstractExpression) :定义解释操作的接口。
  • 终结符表达式(TerminalExpression) :表示语言中的基本元素,如数字或变量。
  • 非终结符表达式(NonTerminalExpression):表示更复杂的语法规则,通过组合终结符表达式和其他非终结符表达式实现。
  • 上下文(Context):存储解释器在解析表达式时需要的全局信息,比如变量值或共享数据。
  • 客户端(Client):构建(或从外部获取)需要解析的表达式,并使用解释器处理表达式。
区分终结符和非终结符主要看它是不是最终的输出,是不是不可再分的组成部分。
案例实现

设计一个动态查询SQL 的解析器,查询SQL模板 + 输入的参数 动态的生成所需要的查询SQL 。
本案例的主要功能

  • 支持占位符替换:如 #{key} 替换为参数值。
  • 支持动态条件解析:如  标签根据条件决定是否生成部分 SQL。
  • 支持集合操作:如  动态生成 IN 子句。
  • 使用解释器模式解析查询SQL 模板,分离模板的不同语义块。
案例类图

1.png

类图简述

  • 上下文 (Context) :存储输入参数,供解释器在解析时访问。
  • 抽象表达式 (SQLExpression) :表示 SQL 模板中的一个语义块,定义 interpret 方法解析该块,参数为Context输入参数。
  • 终结符表达式


  • 文本表达式(TextExpression):不可再分的文本部分(如静态 SQL 片段);
  • 占位符表达式 (PlaceholderExpression):解析并替换 #{key}。

  • 非终结符表达式


  • 条件组表达式(ConditionalGroupExpression):解析 标签中的一组条件;
  • 条件表达式 (IfExpression):解析  标签中的动态 SQL;
  • 集合表达式 (ForEachExpression):解析  动态生成 SQL;
  • 复合表达式(CompositeExpression):将多个表达式组合成一个整体。
上下文

存储动态 SQL 的参数,供解释器在解析时访问。
  1. public class Context {
  2.     private Map<String, Object> parameters;
  3.     public Context(Map<String, Object> parameters) {
  4.         this.parameters = parameters;
  5.     }
  6.     public Object getParameter(String key) {
  7.         return parameters.get(key);
  8.     }
  9. }
复制代码
抽象表达式

表示 SQL 模板中的一个语义块,定义 interpret 方法解析该块。
  1. public interface SQLExpression {
  2.     String interpret(Context context);
  3. }
复制代码
终结符表达式

文本表达式(TextExpression):不可再分的文本部分(如静态 SQL 片段);
占位符表达式 (PlaceholderExpression):解析并替换 #{key}。
  1. // 终结符表达式:文本片段
  2. public class TextExpression implements SQLExpression {
  3.     private String text;
  4.     public TextExpression(String text) {
  5.         this.text = text;
  6.     }
  7.     @Override
  8.     public String interpret(Context context) {
  9.         return text;
  10.     }
  11. }
  12. // 终结符表达式:占位符替换
  13. class PlaceholderExpression implements SQLExpression {
  14.     private String text;
  15.     public PlaceholderExpression(String text) {
  16.         this.text = text;
  17.     }
  18.     @Override
  19.     public String interpret(Context context) {
  20.         // 替换 #{key} 为参数值
  21.         Pattern pattern = Pattern.compile("#\\{(\\w+)}");
  22.         Matcher matcher = pattern.matcher(text);
  23.         StringBuffer result = new StringBuffer();
  24.         while (matcher.find()) {
  25.             String key = matcher.group(1);
  26.             Object value = context.getParameter(key);
  27.             if (value == null) {
  28.                 throw new RuntimeException("参数 " + key + " 未提供");
  29.             }
  30.             String replacement = (value instanceof String) ? "'" + value + "'" : value.toString();
  31.             matcher.appendReplacement(result, replacement);
  32.         }
  33.         matcher.appendTail(result);
  34.         return result.toString();
  35.     }
  36. }
复制代码
非终结符表达式

条件组表达式(ConditionalGroupExpression):解析 标签中的一组条件;
条件表达式 (IfExpression):解析  标签中的动态 SQL;
集合表达式 (ForEachExpression):解析  动态生成 SQL;
复合表达式(CompositeExpression):将多个表达式组合成一个整体。
  1. // 非终结符表达式:条件组,自动管理 WHERE/AND/OR
  2. public class ConditionalGroupExpression implements SQLExpression {
  3.     private List<SQLExpression> conditions = new ArrayList<>();
  4.     public void addCondition(SQLExpression condition) {
  5.         conditions.add(condition);
  6.     }
  7.     @Override
  8.     public String interpret(Context context) {
  9.         StringBuilder result = new StringBuilder();
  10.         int validConditions = 0;
  11.         for (SQLExpression condition : conditions) {
  12.             String conditionResult = condition.interpret(context).trim();
  13.             if (!conditionResult.isEmpty()) {
  14.                 // 对首个有效条件去掉前缀
  15.                 if (validConditions == 0) {
  16.                     if (conditionResult.toUpperCase().startsWith("AND ")) {
  17.                         conditionResult = conditionResult.substring(4);
  18.                     } else if (conditionResult.toUpperCase().startsWith("OR ")) {
  19.                         conditionResult = conditionResult.substring(3);
  20.                     }
  21.                 } else {
  22.                     // 非首条件,确保没有多余的空格或重复逻辑
  23.                     if (!conditionResult.toUpperCase().startsWith("AND") && !conditionResult.toUpperCase().startsWith("OR")) {
  24.                         result.append(" AND ");
  25.                     } else {
  26.                         result.append(" ");
  27.                     }
  28.                 }
  29.                 result.append(conditionResult);
  30.                 validConditions++;
  31.             }
  32.         }
  33.         return validConditions > 0 ? "WHERE " + result.toString().trim() : "";
  34.     }
  35. }
  36. // 非终结符表达式:条件
  37. class IfExpression implements SQLExpression {
  38.     private String condition;
  39.     private SQLExpression innerExpression;
  40.     public IfExpression(String condition, SQLExpression innerExpression) {
  41.         this.condition = condition;
  42.         this.innerExpression = innerExpression;
  43.     }
  44.     @Override
  45.     public String interpret(Context context) {
  46.         // 解析条件,支持 key != null 和 key == value 等
  47.         if (evaluateCondition(condition, context)) {
  48.             return innerExpression.interpret(context);
  49.         }
  50.         return ""; // 条件不满足时返回空字符串
  51.     }
  52.     // 解析条件语法
  53.     private boolean evaluateCondition(String condition, Context context) {
  54.         // 简单支持 key != null 和 key == value 的逻辑
  55.         if (condition.contains("!=")) {
  56.             String[] parts = condition.split("!=");
  57.             String key = parts[0].trim();
  58.             Object value = context.getParameter(key);
  59.             return value != null; // 判断 key 是否存在
  60.         } else if (condition.contains("==")) {
  61.             String[] parts = condition.split("==");
  62.             String key = parts[0].trim();
  63.             String expectedValue = parts[1].trim().replace("'", ""); // 移除单引号
  64.             Object actualValue = context.getParameter(key);
  65.             return expectedValue.equals(actualValue != null ? actualValue.toString() : null);
  66.         }
  67.         throw new RuntimeException("不支持的条件: " + condition);
  68.     }
  69. }
  70. // 非终结符表达式:集合操作
  71. class ForEachExpression implements SQLExpression {
  72.     private String itemName;
  73.     private String collectionKey;
  74.     private String open;
  75.     private String separator;
  76.     private String close;
  77.     public ForEachExpression(String itemName, String collectionKey, String open, String separator, String close) {
  78.         this.itemName = itemName;
  79.         this.collectionKey = collectionKey;
  80.         this.open = open;
  81.         this.separator = separator;
  82.         this.close = close;
  83.     }
  84.     @Override
  85.     public String interpret(Context context) {
  86.         Object collection = context.getParameter(collectionKey);
  87.         if (!(collection instanceof Collection)) {
  88.             throw new RuntimeException("参数 " + collectionKey + " 必须是集合");
  89.         }
  90.         Collection<?> items = (Collection<?>) collection;
  91.         StringJoiner joiner = new StringJoiner(separator, open, close);
  92.         for (Object item : items) {
  93.             joiner.add(item instanceof String ? "'" + item + "'" : item.toString());
  94.         }
  95.         return joiner.toString();
  96.     }
  97. }
  98. // 复合表达式:将多个表达式组合成一个整体
  99. class CompositeExpression implements SQLExpression {
  100.     private List<SQLExpression> expressions = new ArrayList<>();
  101.     public CompositeExpression(SQLExpression... expressions) {
  102.         this.expressions.addAll(Arrays.asList(expressions));
  103.     }
  104.     @Override
  105.     public String interpret(Context context) {
  106.         StringBuilder result = new StringBuilder();
  107.         for (SQLExpression expression : expressions) {
  108.             result.append(expression.interpret(context));
  109.         }
  110.         return result.toString();
  111.     }
  112. }
复制代码
测试客户端

两条动态查询SQL的生成测试
  1. public class DynamicSQLInterpreterDemo {
  2.     public static void main(String[] args) {
  3.         // 动态 SQL 模板
  4.         List<SQLExpression> expressions = new ArrayList<>();
  5.         expressions.add(new TextExpression("SELECT * FROM t_users"));
  6.         // WHERE 条件组
  7.         ConditionalGroupExpression whereGroup = new ConditionalGroupExpression();
  8.         whereGroup.addCondition(new IfExpression("id != null", new PlaceholderExpression("and id = #{id}")));
  9.         whereGroup.addCondition(new IfExpression("name != null", new PlaceholderExpression("OR name = #{name}")));
  10.         whereGroup.addCondition(new IfExpression("ids != null && !ids.isEmpty()", new CompositeExpression(
  11.                 new TextExpression("AND id IN "),
  12.                 new ForEachExpression("id", "ids", "(", ",", ")")
  13.         )));
  14.         expressions.add(whereGroup);
  15.         // 测试参数
  16.         Map<String, Object> parameters1 = new HashMap<>();
  17.         parameters1.put("id", 1);
  18.         parameters1.put("name", "Alice");
  19.         parameters1.put("ids", Arrays.asList(1, 2, 3));
  20.         Map<String, Object> parameters2 = new HashMap<>();
  21.         parameters2.put("ids", Arrays.asList(1, 2, 3));
  22.         // 输出最终 SQL
  23.         System.out.println("测试 1:");
  24.         generateSQL(expressions, parameters1);
  25.         System.out.println("测试 2:");
  26.         generateSQL(expressions, parameters2);
  27.     }
  28.     private static void generateSQL(List<SQLExpression> expressions, Map<String, Object> parameters) {
  29.         Context context = new Context(parameters);
  30.         StringBuilder parsedSQL = new StringBuilder();
  31.         for (SQLExpression expression : expressions) {
  32.             String result = expression.interpret(context).trim();
  33.             if (!result.isEmpty()) {
  34.                 parsedSQL.append(" ").append(result);
  35.             }
  36.         }
  37.         System.out.println(parsedSQL.toString().trim());
  38.     }
  39. }
复制代码
运行结果
  1. 测试 1:
  2. SELECT * FROM t_users WHERE id = 1 OR name = 'Alice' AND id IN (1,2,3)
  3. 测试 2:
  4. SELECT * FROM t_users WHERE id IN (1,2,3)
复制代码
该案例简单实现了动态查询SQL的生成。List expressions 变量在动态SQL表达式只执行两次add()操作,第一次是SELECT * FROM t_user,第二次是where条件语句,然后再根据参数值替换占位符来实现动态SQL的生成。
所有的表达式都可以独立进行扩展调整而不相互影响(灵活性、扩展性)。比如、新增分组查询、查询结果排序等表达式,以及原有表达式的不断优化调整。
也可以,将动态SQL模版改为xml文档进行SQL配置化。解析过程变为:先经过xml文档解析,根据请求参数再进行动态SQL解释,从而灵活生成SQL。这看起来有点MyBatis的味道。
解释器模式的应用

Hibernate:使用 ORM 技术,通过对象关系映射来执行查询;
MyBatis:通过映射器和 XML 配置来处理动态 SQL;
两者都使用了解释器模式来处理查询的解析。
优缺点和适用场景

优点


  • 扩展性好:可以轻松地添加新的表达式解析规则。
  • 直观性强:语言的规则和实现代码一一对应,清晰明了。
  • 适用于领域特定语言:非常适合解决领域特定问题。
缺点


  • 复杂性增加:对于复杂的语法规则,类的数量会迅速增加,导致维护成本高。
  • 性能问题:解释器模式效率较低,特别是在需要解析大量复杂表达式时。
适用场景

解释器模式适合在以下情况下使用:

  • 特定语法或规则:需要为某个特定领域设计一个语言或表达式处理工具。
  • 重复问题:问题可以通过一组标准规则或语法重复解决。
  • 可扩展性需求:希望能够轻松添加新规则或表达式。
常见示例:

  • 计算器程序(解析数学表达式)。
  • SQL解析器。
  • 编译器中的语法解析器。
  • 简单的脚本解释器。
总结

解释器模式适合用于实现轻量级的解析器或简单的领域特定语言,但在面对复杂语法或高性能需求的场景时,可能需要其他更高效的解析工具(如正则表达式、ANTLR等)。
解释器模式提供了一种创建领域特定语言(DSL)的方法。
2.gif

需要查看往期设计模式文章的,可以在个人主页中或者文章开头的集合中查看,可关注我,持续更新中。。。
超实用的SpringAOP实战之日志记录
2023年下半年软考考试重磅消息
通过软考后却领取不到实体证书?
计算机算法设计与分析(第5版)
Java全栈学习路线、学习资源和面试题一条龙
软考证书=职称证书?
软考中级--软件设计师毫无保留的备考分享

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册