Interpreter Pattern(解釋器模式)
是什麼?
Interpreter Pattern 為一種簡單語言定義文法表示,並建立一個解釋器來解讀這個文法中的句子。每條文法規則對應一個類別,組合起來形成語法樹(Syntax Tree),透過遞迴走訪來求值。
ℹ️GoF 分類
Interpreter 屬於行為型模式(Behavioral Pattern),重點在於用物件結構來表示文法規則,並透過遞迴解析來執行。
什麼時候用?
- 你有一個簡單的語言或表達式需要解析和執行
- 文法規則不多,且效能要求不高
- 你需要規則引擎(Rule Engine)來動態執行業務規則
- 你想用 DSL(Domain-Specific Language)來簡化設定
什麼時候不該用?
⚠️過度設計警告
如果文法很複雜(幾十條以上的規則),Interpreter 會產生大量的類別且效能低下。此時用成熟的 Parser Generator(如 ANTLR)或正規表達式更合適。Interpreter Pattern 只適合簡單的語言和表達式。
執行流程
定義文法
用 BNF 或類似符號描述語言的規則
建立 Expression 介面
定義 Interpret 方法
實作終端表達式
文法中不可再分解的基本元素
實作非終端表達式
由其他表達式組合而成的複合規則
建立語法樹並解釋
Client 組合表達式成樹,呼叫 Interpret 求值
流程解讀:Interpreter 的流程分兩階段。建構階段:Client 根據文法規則組合 Expression 物件成樹(如 AND(VIP, OR(HasCoupon, NOT(IsWeekend))))。求值階段:呼叫根節點的 Interpret(context),遞迴走訪整棵樹。TerminalExpression(終端表達式,文法中不可再分解的基本元素如變數、常數)從 Context 讀值,NonTerminalExpression(非終端表達式,由其他表達式組合而成的規則如 AND、OR)遞迴求值子表達式再組合結果。
程式碼範例
C# 版本
// 場景:布林表達式解析器(支援 AND、OR、NOT)
// 1. Expression 介面
public interface IExpression
{
bool Interpret(Dictionary<string, bool> context);
}
// 2. 終端表達式:變數
public class Variable : IExpression
{
private readonly string _name;
public Variable(string name) => _name = name;
public bool Interpret(Dictionary<string, bool> context)
=> context.GetValueOrDefault(_name, false);
public override string ToString() => _name;
}
// 3. 非終端表達式
public class AndExpression : IExpression
{
private readonly IExpression _left, _right;
public AndExpression(IExpression left, IExpression right)
{
_left = left; _right = right;
}
public bool Interpret(Dictionary<string, bool> context)
=> _left.Interpret(context) && _right.Interpret(context);
}
public class OrExpression : IExpression
{
private readonly IExpression _left, _right;
public OrExpression(IExpression left, IExpression right)
{
_left = left; _right = right;
}
public bool Interpret(Dictionary<string, bool> context)
=> _left.Interpret(context) || _right.Interpret(context);
}
public class NotExpression : IExpression
{
private readonly IExpression _expr;
public NotExpression(IExpression expr) => _expr = expr;
public bool Interpret(Dictionary<string, bool> context)
=> !_expr.Interpret(context);
}
// 4. 使用:「VIP AND (HasCoupon OR NOT IsWeekend)」
var vip = new Variable("VIP");
var hasCoupon = new Variable("HasCoupon");
var isWeekend = new Variable("IsWeekend");
// 組合語法樹
IExpression rule = new AndExpression(
vip,
new OrExpression(hasCoupon, new NotExpression(isWeekend))
);
var context = new Dictionary<string, bool>
{
["VIP"] = true,
["HasCoupon"] = false,
["IsWeekend"] = false
};
Console.WriteLine($"可享折扣:{rule.Interpret(context)}"); // True
// VIP=true AND (HasCoupon=false OR NOT IsWeekend=false)
// = true AND (false OR true) = true AND true = trueTypeScript 版本
// 1. Expression 介面
interface Expression {
interpret(context: Map<string, boolean>): boolean;
}
// 2. 終端表達式
class Variable implements Expression {
constructor(private name: string) {}
interpret(context: Map<string, boolean>): boolean {
return context.get(this.name) ?? false;
}
}
// 3. 非終端表達式
class AndExpression implements Expression {
constructor(private left: Expression, private right: Expression) {}
interpret(ctx: Map<string, boolean>): boolean {
return this.left.interpret(ctx) && this.right.interpret(ctx);
}
}
class OrExpression implements Expression {
constructor(private left: Expression, private right: Expression) {}
interpret(ctx: Map<string, boolean>): boolean {
return this.left.interpret(ctx) || this.right.interpret(ctx);
}
}
class NotExpression implements Expression {
constructor(private expr: Expression) {}
interpret(ctx: Map<string, boolean>): boolean {
return !this.expr.interpret(ctx);
}
}
// 4. 使用
const vip = new Variable("VIP");
const hasCoupon = new Variable("HasCoupon");
const isWeekend = new Variable("IsWeekend");
const rule: Expression = new AndExpression(
vip,
new OrExpression(hasCoupon, new NotExpression(isWeekend))
);
const context = new Map<string, boolean>([
["VIP", true],
["HasCoupon", false],
["IsWeekend", false],
]);
console.log(`可享折扣:${rule.interpret(context)}`); // truePython 版本
from abc import ABC, abstractmethod
# 1. Expression 介面
class Expression(ABC):
@abstractmethod
def interpret(self, context: dict[str, bool]) -> bool:
pass
# 2. 終端表達式
class Variable(Expression):
def __init__(self, name: str):
self.name = name
def interpret(self, context: dict[str, bool]) -> bool:
return context.get(self.name, False)
# 3. 非終端表達式
class AndExpression(Expression):
def __init__(self, left: Expression, right: Expression):
self._left = left
self._right = right
def interpret(self, context: dict[str, bool]) -> bool:
return self._left.interpret(context) and self._right.interpret(context)
class OrExpression(Expression):
def __init__(self, left: Expression, right: Expression):
self._left = left
self._right = right
def interpret(self, context: dict[str, bool]) -> bool:
return self._left.interpret(context) or self._right.interpret(context)
class NotExpression(Expression):
def __init__(self, expr: Expression):
self._expr = expr
def interpret(self, context: dict[str, bool]) -> bool:
return not self._expr.interpret(context)
# 4. 使用
vip = Variable("VIP")
has_coupon = Variable("HasCoupon")
is_weekend = Variable("IsWeekend")
rule = AndExpression(
vip,
OrExpression(has_coupon, NotExpression(is_weekend))
)
context = {"VIP": True, "HasCoupon": False, "IsWeekend": False}
print(f"可享折扣:{rule.interpret(context)}") # TrueJava 版本
import java.util.Map;
// 1. Expression 介面
public interface Expression {
boolean interpret(Map<String, Boolean> context);
}
// 2. 終端表達式
public class Variable implements Expression {
private final String name;
public Variable(String name) { this.name = name; }
public boolean interpret(Map<String, Boolean> context) {
return context.getOrDefault(name, false);
}
}
// 3. 非終端表達式
public class AndExpression implements Expression {
private final Expression left, right;
public AndExpression(Expression l, Expression r) { left = l; right = r; }
public boolean interpret(Map<String, Boolean> ctx) {
return left.interpret(ctx) && right.interpret(ctx);
}
}
public class OrExpression implements Expression {
private final Expression left, right;
public OrExpression(Expression l, Expression r) { left = l; right = r; }
public boolean interpret(Map<String, Boolean> ctx) {
return left.interpret(ctx) || right.interpret(ctx);
}
}
public class NotExpression implements Expression {
private final Expression expr;
public NotExpression(Expression e) { expr = e; }
public boolean interpret(Map<String, Boolean> ctx) {
return !expr.interpret(ctx);
}
}
// 4. 使用
Variable vip = new Variable("VIP");
Variable hasCoupon = new Variable("HasCoupon");
Variable isWeekend = new Variable("IsWeekend");
Expression rule = new AndExpression(
vip,
new OrExpression(hasCoupon, new NotExpression(isWeekend))
);
Map<String, Boolean> context = Map.of(
"VIP", true, "HasCoupon", false, "IsWeekend", false
);
System.out.println("可享折扣:" + rule.interpret(context)); // true結構圖
結構解讀:Client 把文法規則組合成一棵語法樹(Syntax Tree,指用樹狀結構表示文法中各元素之間的從屬關係)。TerminalExpression(Variable)是葉節點,直接從 Context 讀取值。NonTerminalExpression(And、Or)是內部節點,遞迴呼叫子表達式的 Interpret() 來求值。整棵樹的求值從根節點開始,遞迴往下直到所有葉節點回傳值。
實戰補充
💡資深開發者經驗
規則引擎:電商的促銷規則、保險的核保條件、權限管理的 Policy 都可以用 Interpreter 來實作。把業務規則表示成表達式樹,業務人員可以動態修改規則而不用改程式碼。
SQL Parser:SQL 語句的 WHERE 條件就是一棵表達式樹。ORM 的 LINQ to SQL、QueryDSL 內部都用類似 Interpreter 的結構來把表達式轉換成 SQL。
正則表達式引擎:正規表達式引擎的核心就是 Interpreter Pattern。每個語法元素(字元、量詞、群組)對應一個 Expression 類別。
Roslyn 的 Syntax Tree:C# 的 Roslyn 編譯器把原始碼解析成 Syntax Tree,每個節點對應一條文法規則。這是 Interpreter Pattern 在工業級的應用,但 Roslyn 用 Visitor Pattern 來走訪而非直接 Interpret。
理解測驗
🤔 Interpreter Pattern 中,終端表達式(Terminal Expression)和非終端表達式(Non-Terminal Expression)的差異是什麼?
🤔 什麼情況下不該使用 Interpreter Pattern?
🤔 以下哪個是 Interpreter Pattern 在實際應用中的例子?
面試常見問題
Q: Interpreter Pattern 在實務中常見嗎?什麼場景最實用?
A: 直接手寫 Interpreter Pattern 不常見,但其概念無處不在。最實用的場景是「規則引擎」— 電商的促銷規則、保險的核保條件、權限的 Policy 表達式。這些場景需要業務人員能動態修改規則而不用改程式碼。常見做法是把規則存在資料庫中(如 JSON/DSL 格式),程式讀取後建構表達式樹來求值。
Q: Interpreter Pattern 為什麼效能不好?有什麼替代方案?
A: 因為每次求值都要遞迴走訪整棵語法樹,且每個節點都是一個物件(物件分配和虛擬方法呼叫的開銷)。替代方案:(1) ANTLR 等 Parser Generator — 自動產生高效的解析器。(2) 編譯成中間碼(Bytecode)再用虛擬機執行。(3) 用 C# 的 Expression Tree + Compile() 把表達式編譯成委派,效能接近原生程式碼。
相關模式
| 模式 | 關係 | |------|------| | Composite | Interpreter 的語法樹本質上就是 Composite 結構 — NonTerminal 包含子 Expression,Terminal 是葉節點 | | Visitor | 當對語法樹的操作種類多時(求值、優化、轉譯),用 Visitor 比在 Expression 中堆方法更好 | | Iterator | 用來走訪語法樹的節點 | | Flyweight | Terminal Expression 如果出現頻繁(如同一個變數被引用多次),可以用 Flyweight 共享 |
重點整理
💡一句話記住
Interpreter Pattern = 自訂小語言的計算機:把規則變成樹,遞迴求值。 口訣:「簡單文法用 Interpreter,複雜文法用 Parser Generator」
| 概念 | 說明 | |------|------| | Expression(表達式介面) | 定義 Interpret 方法 | | TerminalExpression(終端) | 文法中最基本的元素(變數、常數) | | NonTerminalExpression(非終端) | 由其他表達式組合的複合規則 | | Context(上下文) | 提供解釋時需要的外部資訊 | | 核心好處 | 簡單語言可以用物件結構表示,易於擴展新規則 | | 代價 | 複雜文法產生大量類別,效能不佳 |