解释器模式 发表于 2025-07-16 更新于 2025-07-16
设计模式 解释器模式 Rif 2025-07-16 2025-07-16 设计模式笔记:解释器模式 (Interpreter Pattern) 一、一句话概括 给定一种语言,定义它的文法的一种表示 ,并定义一个解释器 ,这个解释器使用该表示来解释语言中的句子。
二、为什么需要它 (Why - The Pain Point) 在某些特定领域,你可能需要处理一些简单、重复的指令或表达式。例如:
搜索引擎 :用户输入 “cat AND (dog OR bird)”。
数学计算器 :处理 “3 + 4 * 2”。
机器人控制 :解析 “MOVE FORWARD 10; TURN RIGHT;”。
痛点: 如果为每一种可能的表达式都写一堆 if-else 或 switch 逻辑来处理,代码会变得极其复杂、难以维护和扩展。你需要一种结构化的方式来表示和解析这些“迷你语言”。
核心问题 :如何为一个简单的语言构建一个解析引擎,使其能够轻松地解释和执行该语言的指令?
三、它是什么 (What - The Solution) 解释器模式建议为语言中的每条语法规则 创建一个类。然后,将一个句子表示成一个由这些类的实例构成的抽象语法树(AST, Abstract Syntax Tree) 。最后,通过调用树根节点的 interpret() 方法来解释整个句子。
核心思想和角色:
**抽象表达式 (AbstractExpression)**:
声明一个 interpret() 方法,所有语法树中的节点(终结符和非终-结符)都实现这个接口。
**终结符表达式 (TerminalExpression)**:
实现了 AbstractExpression 接口,代表了语言中的“终结符”(即最基本的元素,不能再被分解)。例如,数字、变量名。
它的 interpret() 方法通常是返回自身的值。
**非终结符表达式 (NonterminalExpression)**:
也实现了 AbstractExpression 接口,代表了语言中的“非终结符”(即语法规则,如加法、减法)。
它通常包含对其他 AbstractExpression(它的子节点)的引用。
它的 interpret() 方法会递归地调用其子节点的 interpret() 方法,并组合它们的结果。
**上下文 (Context)**:
包含一些解释器需要用到的全局信息,比如变量的值映射表。
**客户端 (Client)**:
负责构建代表特定句子的抽象语法树,并调用其 interpret() 方法。
四、怎么做 (How - The Blueprint) 基本步骤(以计算 “a + b - c” 为例):
创建 Expression 接口,包含 interpret(Context context) 方法。
创建终结符表达式 NumberExpression,代表一个数字或变量。
创建非终结符表达式 AddExpression 和 SubtractExpression。每个都持有两个 Expression 的引用(左表达式和右表达式)。
客户端根据输入的字符串,手动或通过一个解析器来构建语法树:new SubtractExpression(new AddExpression(new NumberExpression("a"), new NumberExpression("b")), new NumberExpression("c"))。
创建 Context,放入 a, b, c 的值。
调用根节点的 interpret() 方法。
Java 示例代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import java.util.Map;import java.util.HashMap;interface Expression { int interpret (Map<String, Integer> context) ; } class NumberExpression implements Expression { private String variable; public NumberExpression (String variable) { this .variable = variable; } @Override public int interpret (Map<String, Integer> context) { return context.get(variable); } } class AddExpression implements Expression { private Expression left, right; public AddExpression (Expression left, Expression right) { this .left = left; this .right = right; } @Override public int interpret (Map<String, Integer> context) { return left.interpret(context) + right.interpret(context); } } class SubtractExpression implements Expression { private Expression left, right; public SubtractExpression (Expression left, Expression right) { this .left = left; this .right = right; } @Override public int interpret (Map<String, Integer> context) { return left.interpret(context) - right.interpret(context); } } public class InterpreterPatternDemo { public static void main (String[] args) { Map<String, Integer> context = new HashMap <>(); context.put("a" , 10 ); context.put("b" , 5 ); context.put("c" , 2 ); Expression expression = new SubtractExpression ( new AddExpression ( new NumberExpression ("a" ), new NumberExpression ("b" ) ), new NumberExpression ("c" ) ); int result = expression.interpret(context); System.out.println("Result of 'a + b - c' is: " + result); } }
五、UML 类图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @startuml skinparam classAttributeIconSize 0 hide empty members interface Expression { + int interpret(Context context) } class NumberExpression implements Expression class AddExpression implements Expression class SubtractExpression implements Expression AddExpression o-- "2" Expression SubtractExpression o-- "2" Expression class InterpreterPatternDemo { } InterpreterPatternDemo ..> Expression : builds & uses @enduml
六、优缺点分析 优点 (Pros):
易于扩展语法 :增加新的语法规则只需要增加新的表达式类,符合开闭原则。
结构清晰 :用类来表示语法规则,使得文法结构非常清晰,易于实现。
缺点 (Cons):
复杂性高 :对于每条语法规则都需要创建一个类,当文法非常复杂时,会导致类的数量急剧增加。
性能问题 :解释过程通常涉及到大量的递归调用,效率可能不高。
适用场景有限 :只适用于定义和解释一些简单、重复的“迷你语言”。对于复杂的文法(如编程语言),通常会使用更专业的工具(如 ANTLR, JavaCC)。