软件设计 解释器模式 Interpreter Pattern

作者:@kuaiquxie
作者的github:https://github.com/bitebita
本文为作者原创,如需转载,请注明出处:https://www.cnblogs.com/dzwj/p/17111649.html


就比如编译器可以将源码编译解释为机器码, 让 CPU 能进行识别并运行。解释器模式的作用其实与编译器一样,都是将一些固定的语法进行解释,构建出一个解释句子的解释器。

解释器是一个语法分析工具,它可以识别句子语义,分离终结符号和非终结符号,提取出需要的信息,针对不同的信息做出相应的处理。其核心思想是识别语法,构建解释

解释器模式主要包含 4 种角色:

  • 抽象表达式(Expression) :负责定义解释方法 interpret, 交由子类进行具体解释。

  • 终结符表达式(Terminal Expression) :实现文法中与终结符有关的解释操作。

    文法中的每一个终结符都有一个具体终结表达式与之相对应,比如公式 R = R1 + R2,R1 和 R2 就是终结符,对应的解析 R1 和 R2 的解释器就是终结符表达式。通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。

  • 非终结符表达式(Nonterminal Expression):实现文法中与非终结符有关的解释操作。

    文法中的每条规则都对应于一个非终结符表达式。非终结符表达式一般是文法中的运算符或者其他关键字,比如公式 R = R1 + R2 中, + 就是非终结符,解析它的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。

  • 上下文环境(Context) :包含解释器之外的全局信息。

    它的任务一般是用来存放文法中各个终结符所对应的具体值,比如 R = R1 + R2,给 R1 赋值 100,给 R2 赋值 200,这些信息需要存放到环境中。

 

代码实现:

这里以根据乘客年龄和身高来判断乘坐公交车是否免费为例介绍解释器模式:

1、定义乘客

/**
* 乘客
*/
public class Passenger {
   /**
    * 姓名
    */
   private String name;
   /**
    * 年龄
    */
   private Integer age;
   /**
    * 身高
    */
   private Double height;
   public Passenger(String name, int age, double height) {
       this.name = name;
       this.age = age;
       this.height = height;
  }
   public String getName() {
       return name;
  }
   public void setName(String name) {
       this.name = name;
  }
   public Integer getAge() {
       return age;
  }
   public void setAge(Integer age) {
       this.age = age;
  }
   public Double getHeight() {
       return height;
  }
   public void setHeight(Double height) {
       this.height = height;
  }
}
2、定义表达式

/**
* 表达式
*/
public interface Expression {
   /**
    * 解释年龄
    * @param age 年龄
    * @return 解释结果
    */
   boolean interpret(int age);
   /**
    * 解释身高
    * @param height 身高
    * @return 解释结果
    */
   boolean interpret(double height);
}
3、定义比较器

/**
* 比较器
*/
public enum Compare {
   /**
    * 较大
    */
   GT,
   /**
    * 相等
    */
   EQ,
   /**
    * 较小
    */
   LT
}
4、定义终结符表达式

/**
* 终结符表达式
*/
public class TerminalExpression implements Expression {
   /**
    * 年龄
    */
   private Integer age;
   /**
    * 身高
    */
   private Double height;
   /**
    * 比较器
    */
   private final Compare compare;
   /**
    * 构造年龄比较
    * @param age 年龄
    * @param compare 比较器
    */
   public TerminalExpression(int age, Compare compare) {
       this.age = age;
       this.compare = compare;
  }
   /**
    * 构造身高比较
    * @param height 身高
    * @param compare 比较器
    */
   public TerminalExpression(double height, Compare compare) {
       this.height = height;
       this.compare = compare;
  }
   @Override
   public boolean interpret(int age) {
       // 比较年龄大小
       switch (compare) {
           // 较大
           case GT:
               return age > this.age;
           // 相等
           case EQ:
               return age == this.age;
           // 较小
           case LT:
               return age < this.age;
           default:
               return false;
      }
  }
   @Override
   public boolean interpret(double height) {
       // 比较身高大小
       switch (compare) {
           // 较大
           case GT:
               return height > this.height;
           // 相等
           case EQ:
               return height == this.height;
           // 较小
           case LT:
               return height < this.height;
           default:
               return false;
      }
  }
}
5、定义非终结符表达式

与表达式:

/**
* 与表达式
*/
public class AndExpression implements Expression {
   /**
    * 表达式1
    */
   private Expression expression1;
   /**
    * 表达式2
    */
   private Expression expression2;
   /**
    * 构造表达式
    * @param expression1 表达式1
    * @param expression2 表达式2
    */
   public AndExpression(Expression expression1, Expression expression2) {
       this.expression1 = expression1;
       this.expression2 = expression2;
  }
   @Override
   public boolean interpret(int age) {
       return this.expression1.interpret(age) && this.expression2.interpret(age);
  }
   @Override
   public boolean interpret(double height) {
       return this.expression1.interpret(height) && this.expression2.interpret(height);
  }
}
或表达式:

/**
* 或表达式
*/
public class OrExpression implements Expression {
   /**
    * 表达式1
    */
   private Expression expression1;
   /**
    * 表达式2
    */
   private Expression expression2;
   /**
    * 构造表达式
    * @param expression1 表达式1
    * @param expression2 表达式2
    */
   public OrExpression(Expression expression1, Expression expression2) {
       this.expression1 = expression1;
       this.expression2 = expression2;
  }
   @Override
   public boolean interpret(int age) {
       return this.expression1.interpret(age) || this.expression2.interpret(age);
  }
   @Override
   public boolean interpret(double height) {
       return this.expression1.interpret(height) || this.expression2.interpret(height);
  }
}
6、定义免费标准

/**
* 免费标准
*/
public class Free {
   /**
    * 年龄表达式
    */
   private Expression ageExpression;
   /**
    * 身高表达式
    */
   private Expression heightExpression;
   /**
    * 构造免费情况
    * @param age 年龄
    * @param height 身高
    */
   public Free(int age, double height) {
       // 大于等于设定年龄
       Expression expression1 = new TerminalExpression(age, Compare.GT);
       Expression expression2 = new TerminalExpression(age, Compare.EQ);
       ageExpression = new OrExpression(expression1, expression2);
       // 小于等于设定身高
       expression1 = new TerminalExpression(height, Compare.LT);
       expression2 = new TerminalExpression(height, Compare.EQ);
       heightExpression = new OrExpression(expression1, expression2);
  }
   /**
    * 结果
    * @param age 年龄
    * @param height 身高
    * @return 判定结果
    */
   public boolean result(int age, double height) {
       return ageExpression.interpret(age) || heightExpression.interpret(height);
  }
}
7、客户端调用

// 定义乘客集合
List<Passenger> list = new ArrayList<>();
Passenger p1 = new Passenger("张三", 65, 170.0);
Passenger p2 = new Passenger("李四", 10, 130.0);
Passenger p3 = new Passenger("王五", 50, 170.0);
list.add(p1);
list.add(p2);
list.add(p3);
// 所有年龄大于等于65或者身高小于等于130的乘客免费乘车
for (Passenger p : list) {
   // 定义免费标准
   Free free = new Free(65, 130);
   // 满足条件则免费
   if (free.result(p.getAge(), p.getHeight())) {
       System.out.println(p.getName() + ":免费");
  }
   // 不满足条件则正常收费
   else {
       System.out.println(p.getName() + ":刷卡或投币");
  }
}

输出结果:

张三:免费
李四:免费
王五:刷卡或投币
   
   
可以看到,按照预期输出了结果,实现了根据年龄和身高自动判断是否免费的功能。

 

优缺点:

优点

1、扩展性好。解释器模式中使用类来表示语言的文法规则,可以比较方便的通过继承等机制来改变或扩展文法。

2、容易实现。在语法树中的每个表达式节点类都是相似的,实现其文法较为容易。

缺点

1、执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。

2、会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护,因此可应用的场景比较少。

 

使用场景:

JDK 源码中的 Pattern 对正则表达式的编译和解析

 
posted @   kuaiquxie  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
点击右上角即可分享
微信分享提示