Interpreter Pattern(解釋器模式)

是什麼?

Interpreter Pattern 為一種簡單語言定義文法表示,並建立一個解釋器來解讀這個文法中的句子。每條文法規則對應一個類別,組合起來形成語法樹(Syntax Tree),透過遞迴走訪來求值。

ℹ️GoF 分類

Interpreter 屬於行為型模式(Behavioral Pattern),重點在於用物件結構來表示文法規則,並透過遞迴解析來執行。

什麼時候用?

什麼時候不該用?

⚠️過度設計警告

如果文法很複雜(幾十條以上的規則),Interpreter 會產生大量的類別且效能低下。此時用成熟的 Parser Generator(如 ANTLR)或正規表達式更合適。Interpreter Pattern 只適合簡單的語言和表達式。

執行流程

1

定義文法

用 BNF 或類似符號描述語言的規則

2

建立 Expression 介面

定義 Interpret 方法

3

實作終端表達式

文法中不可再分解的基本元素

4

實作非終端表達式

由其他表達式組合而成的複合規則

5

建立語法樹並解釋

Client 組合表達式成樹,呼叫 Interpret 求值

流程解讀:Interpreter 的流程分兩階段。建構階段:Client 根據文法規則組合 Expression 物件成樹(如 AND(VIP, OR(HasCoupon, NOT(IsWeekend))))。求值階段:呼叫根節點的 Interpret(context),遞迴走訪整棵樹。TerminalExpression(終端表達式,文法中不可再分解的基本元素如變數、常數)從 Context 讀值,NonTerminalExpression(非終端表達式,由其他表達式組合而成的規則如 AND、OR)遞迴求值子表達式再組合結果。

程式碼範例

C# 版本

csharp
// 場景:布林表達式解析器(支援 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 = true

TypeScript 版本

typescript
// 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)}`); // true

Python 版本

python
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)}")  # True

Java 版本

java
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
builds syntax tree
Expression Interface
reads from
TerminalExpression (Variable)
implements
NonTerminal (And)
implements
NonTerminal (Or)
implements
Context (Variables Map)

結構解讀: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(上下文) | 提供解釋時需要的外部資訊 | | 核心好處 | 簡單語言可以用物件結構表示,易於擴展新規則 | | 代價 | 複雜文法產生大量類別,效能不佳 |

你可能也想看

Memento Pattern回到目錄 →

按 ← → 鍵切換課程