0133_解释器模式(Interpreter)
解释器模式(Interpreter)
意图
给定一种语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
UML 图

优点
- 易于扩展文法:由于使用类来表示语言的文法规则,可以通过继承来扩展文法
- 易于实现文法:实现文法中的每个规则都很简单,可以方便地改变和扩展文法
- 增加新的解释表达式方便:增加新的解释表达式不需要修改现有表达式
- 可组合性:可以通过组合多个表达式来构建复杂的解释器
缺点
- 复杂文法难以维护:对于复杂的文法,解释器模式会定义过多的类,导致系统变得复杂
- 执行效率较低:由于通常使用递归调用,解释器的执行效率可能较低
- 应用场景有限:适用于简单的文法,复杂文法需要使用其他技术如编译器或解析器生成器
- 难以扩展复杂操作:添加新的操作需要修改所有表达式类
代码示例
以机器人指令解释系统为例:
1. 抽象表达式接口 (Abstract Expression)
// 抽象表达式接口
public interface RobotExpression {
int interpret(RobotContext context);
String getDescription();
}
2. 终结符表达式 (Terminal Expressions)
// 移动指令
public class MoveExpression implements RobotExpression {
private int distance;
public MoveExpression(int distance) {
this.distance = distance;
}
@Override
public int interpret(RobotContext context) {
context.addToLog("移动 " + distance + " 米");
return distance;
}
@Override
public String getDescription() {
return "移动 " + distance + " 米";
}
}
// 转向指令
public class TurnExpression implements RobotExpression {
private String direction;
public TurnExpression(String direction) {
this.direction = direction;
}
@Override
public int interpret(RobotContext context) {
context.addToLog("转向 " + direction);
return 0; // 转向不消耗能量
}
@Override
public String getDescription() {
return "转向 " + direction;
}
}
// 抓取指令
public class GrabExpression implements RobotExpression {
private String object;
public GrabExpression(String object) {
this.object = object;
}
@Override
public int interpret(RobotContext context) {
context.addToLog("抓取 " + object);
return 5; // 抓取消耗5单位能量
}
@Override
public String getDescription() {
return "抓取 " + object;
}
}
3. 非终结符表达式 (Nonterminal Expressions)
// 重复指令
public class RepeatExpression implements RobotExpression {
private int times;
private RobotExpression expression;
public RepeatExpression(int times, RobotExpression expression) {
this.times = times;
this.expression = expression;
}
@Override
public int interpret(RobotContext context) {
int totalEnergy = 0;
context.addToLog("开始重复 " + times + " 次");
for (int i = 0; i < times; i++) {
context.addToLog("第 " + (i+1) + " 次执行:");
totalEnergy += expression.interpret(context);
}
context.addToLog("重复执行完成");
return totalEnergy;
}
@Override
public String getDescription() {
return "重复 " + times + " 次: " + expression.getDescription();
}
}
// 序列指令
public class SequenceExpression implements RobotExpression {
private List<RobotExpression> expressions = new ArrayList<>();
public void addExpression(RobotExpression expression) {
expressions.add(expression);
}
@Override
public int interpret(RobotContext context) {
int totalEnergy = 0;
context.addToLog("开始执行序列指令");
for (RobotExpression expression : expressions) {
totalEnergy += expression.interpret(context);
}
context.addToLog("序列指令执行完成");
return totalEnergy;
}
@Override
public String getDescription() {
StringBuilder sb = new StringBuilder("序列指令: [");
for (int i = 0; i < expressions.size(); i++) {
if (i > 0) sb.append(", ");
sb.append(expressions.get(i).getDescription());
}
sb.append("]");
return sb.toString();
}
}
4. 上下文类 (Context)
// 上下文类
public class RobotContext {
private int x = 0;
private int y = 0;
private String direction = "北";
private List<String> log = new ArrayList<>();
private int energyConsumed = 0;
public void addToLog(String message) {
log.add(message);
}
public List<String> getLog() {
return new ArrayList<>(log);
}
public void clearLog() {
log.clear();
}
public void addEnergyConsumed(int energy) {
this.energyConsumed += energy;
}
public int getEnergyConsumed() {
return energyConsumed;
}
public String getPosition() {
return "(" + x + ", " + y + ")";
}
public String getDirection() {
return direction;
}
// 更新位置和方向的方法
public void updatePosition(int distance) {
switch (direction) {
case "北": y += distance; break;
case "南": y -= distance; break;
case "东": x += distance; break;
case "西": x -= distance; break;
}
}
public void updateDirection(String newDirection) {
this.direction = newDirection;
}
}
5. 指令解析器 (Parser)
// 指令解析器
public class RobotInstructionParser {
public static RobotExpression parse(String instruction) {
if (instruction.startsWith("MOVE")) {
int distance = Integer.parseInt(instruction.substring(5));
return new MoveExpression(distance);
}
else if (instruction.startsWith("TURN")) {
String dir = instruction.substring(5);
return new TurnExpression(dir);
}
else if (instruction.startsWith("GRAB")) {
String obj = instruction.substring(5);
return new GrabExpression(obj);
}
else if (instruction.startsWith("REPEAT")) {
String[] parts = instruction.split(" ");
int times = Integer.parseInt(parts[1]);
String subInstruction = instruction.substring(instruction.indexOf(' ', 7) + 1);
RobotExpression subExpression = parse(subInstruction);
return new RepeatExpression(times, subExpression);
}
else if (instruction.startsWith("SEQUENCE")) {
SequenceExpression sequence = new SequenceExpression();
String[] subInstructions = instruction.substring(9).split(";");
for (String subInstruction : subInstructions) {
sequence.addExpression(parse(subInstruction.trim()));
}
return sequence;
}
throw new IllegalArgumentException("无法解析指令: " + instruction);
}
}
6. 客户端代码
public class InterpreterPatternDemo {
public static void main(String[] args) {
System.out.println("=== 解释器模式演示 - 机器人指令系统 ===\n");
// 创建上下文
RobotContext context = new RobotContext();
// 解析并执行简单指令
System.out.println("1. 执行简单指令:");
RobotExpression move = RobotInstructionParser.parse("MOVE 10");
RobotExpression turn = RobotInstructionParser.parse("TURN 东");
RobotExpression grab = RobotInstructionParser.parse("GRAB 箱子");
System.out.println("指令: " + move.getDescription());
int energy1 = move.interpret(context);
System.out.println("消耗能量: " + energy1 + "\n");
System.out.println("指令: " + turn.getDescription());
int energy2 = turn.interpret(context);
System.out.println("消耗能量: " + energy2 + "\n");
System.out.println("指令: " + grab.getDescription());
int energy3 = grab.interpret(context);
System.out.println("消耗能量: " + energy3 + "\n");
// 解析并执行复杂指令
System.out.println("2. 执行复杂指令:");
RobotExpression repeat = RobotInstructionParser.parse("REPEAT 3 MOVE 5");
System.out.println("指令: " + repeat.getDescription());
context.clearLog();
int energy4 = repeat.interpret(context);
// 显示执行日志
System.out.println("\n执行日志:");
for (String logEntry : context.getLog()) {
System.out.println(" " + logEntry);
}
System.out.println("总消耗能量: " + energy4 + "\n");
// 解析并执行序列指令
System.out.println("3. 执行序列指令:");
RobotExpression sequence = RobotInstructionParser.parse(
"SEQUENCE MOVE 8; TURN 南; MOVE 5; GRAB 物品");
System.out.println("指令: " + sequence.getDescription());
context.clearLog();
int energy5 = sequence.interpret(context);
System.out.println("\n执行日志:");
for (String logEntry : context.getLog()) {
System.out.println(" " + logEntry);
}
System.out.println("总消耗能量: " + energy5);
System.out.println("最终位置: " + context.getPosition());
System.out.println("最终方向: " + context.getDirection());
System.out.println("\n=== 演示完成 ===");
}
}
在Spring的应用
在Spring框架中,解释器模式的应用主要体现在:
- SpEL (Spring Expression Language):Spring表达式语言使用解释器模式来解析和求值表达式
- 配置文件解析:Spring使用解释器模式解析XML配置文件和注解
- 路由表达式:在Spring Integration中,使用解释器模式解析路由表达式
- 条件注解:如
@Conditional注解使用解释器模式解析条件表达式
总结
解释器模式适用于需要解释执行特定语言的场景,特别是当语言比较简单且执行效率不是关键因素时。它通过将语言的每个规则表示为一个类,使得语言的文法易于改变和扩展。
在机器人指令系统的例子中,我们展示了如何:
- 定义抽象表达式接口和具体表达式类
- 实现终结符表达式(基本指令)和非终结符表达式(复合指令)
- 使用上下文对象维护解释过程中的状态信息
- 实现指令解析器将字符串指令转换为表达式对象
解释器模式的优点在于它的灵活性和可扩展性,但需要注意它可能导致的类膨胀问题和性能问题。在实际应用中,对于复杂的语言,通常会将解释器模式与编译器技术结合使用。

浙公网安备 33010602011771号