1 解释器模式介绍
解释器模式是一种行为型设计模式,它提供了一种方法来解释语言、表达式或符号。
在该模式中,定义了一个表达式接口,并实现了对应的表达式类,这些类可以解释不同的符号组成的表达式,从而实现对语言的解释。
📌 场景
这种模式常常被用在编译器、解释器和正则表达式库中。该模式的核心思想是将一个复杂的问题分解成许多简单的问题,并将这些问题之间的关系表示为一种语法规则。
📌 优点
- 灵活性好:解释器模式能够灵活地处理复杂的或变化的语法规则,因为语法规则都是由解释器来进行解释和处理的。
- 易于修改和扩展:由于解释器模式中的文法以类的形式存在,所以增加新的文法规则和解释器相对容易。
📌 缺点
- 执行效率较低:因为使用解释器模式需要对文法规则进行解析和执行,而这个过程是比较耗时的,所以解释器模式的执行效率较低。
- 增加系统复杂性:因为解释器模式中的文法规则以类的形式存在,所以随着文法规则的增加,类的数量也会随之增加,这会增加系统的复杂性。
- 可维护性较差:由于解释器模式中的文法规则以类的形式存在,如果系统中存在大量的文法规则,那么系统的维护难度也会相应增加。
📌 注意
解释器模式在实际的软件开发中使用比较少,因为它会引起效率、性能以及维护等问题。如果碰到对表达式的解释,在 Java 中可以用 Expression4J 或 Jep 等来设计。
📌 四大角色
- 抽象表达式(Expression) :负责定义解释方法
interpret
, 交由子类进行具体解释。 - 终结符表达式(Terminal Expression) :实现文法中与终结符有关的解释操作。文法中的每一个终结符都有一个具体终结表达式与之相对应,比如公式 R = R1 + R2,R1 和 R2 就是终结符,对应的解析 R1 和 R2 的解释器就是终结符表达式。通常一个解释器模式中只有一个终结符表达式,但有多个实例,对应不同的终结符。
- 非终结符表达式(Nonterminal Expression):实现文法中与非终结符有关的解释操作。文法中的每条规则都对应于一个非终结符表达式。非终结符表达式一般是文法中的运算符或者其他关键字,比如公式 R = R1 + R2 中, + 就是非终结符,解析它的解释器就是一个非终结符表达式。非终结符表达式根据逻辑的复杂程度而增加,原则上每个文法规则都对应一个非终结符表达式。
- 上下文环境(Context) :包含解释器之外的全局信息。它的任务一般是用来存放文法中各个终结符所对应的具体值,比如 R = R1 + R2,给 R1 赋值 100,给 R2 赋值 200,这些信息需要存放到环境中。
2 解释器模式实现
以根据乘客年龄和身高来判断乘坐公交车是否免费为例:
📌 1.定义乘客
/**
* 乘客
*/
@Data
@AllArgsConstructor
public class Passenger {
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private Integer age;
/**
* 身高
*/
private Double 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 adjust(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);
list.forEach(p->{
// 定义免费标准
Free free = new Free(65, 130);
// 满足条件则免费
if (free.adjust(p.getAge(), p.getHeight())) {
System.out.println(p.getName() + ":免费");
}// 不满足条件则正常收费
else {
System.out.println(p.getName() + ":刷卡或投币");
}
});
控制台输出:
可以看到,这里按照预期输出了结果,实现了根据年龄和身高自动判断是否免费的功能。