設計模式:解釋器模式 (Interpreter)

8 分鐘
軟體設計設計模式OOP

Interpreter 為一種語言定義文法,並把它的句子對應成一棵物件 —— 個節點都知道如何解釋自己。


意圖與用途

當一種簡單語言需要被求值或執行時,可以把它的文法表示成一個層次化的物件結構 (也就是 AST),每一種節點都知道如何解釋自己。

Interpreter 在 GoF 模式中相對少被使用。只有當你要定義一種小而受控的語言時,才值得動用它:例如設定用的 DSL、簡單的規則引擎,或一組小型指令集。


結構與角色

  • AbstractExpression:定義 interpret(context) 的介面
  • TerminalExpression:沒有子表達式的終端符號 (數字、變數)
  • NonTerminalExpression:包含其他表達式的組合式 (加法、乘法)
  • Context:解釋過程中可取用的外部狀態

實作範例:算術表達式計算器

TypeScript
type Context = Map;

interface Expression {
  interpret(context: Context): number;
}

// TerminalExpression:數字常數
class NumberExpression implements Expression {
  constructor(private value: number) {}

  interpret(_context: Context): number {
    return this.value;
  }
}

// TerminalExpression:變數
class VariableExpression implements Expression {
  constructor(private name: string) {}

  interpret(context: Context): number {
    const value = context.get(this.name);
    if (value === undefined) throw new Error(`未定義變數: ${this.name}`);
    return value;
  }
}

// NonTerminalExpression:加法
class AddExpression implements Expression {
  constructor(
    private left: Expression,
    private right: Expression,
  ) {}

  interpret(context: Context): number {
    return this.left.interpret(context) + this.right.interpret(context);
  }
}

// NonTerminalExpression:乘法
class MultiplyExpression implements Expression {
  constructor(
    private left: Expression,
    private right: Expression,
  ) {}

  interpret(context: Context): number {
    return this.left.interpret(context) * this.right.interpret(context);
  }
}

// NonTerminalExpression:負號
class NegateExpression implements Expression {
  constructor(private expression: Expression) {}

  interpret(context: Context): number {
    return -this.expression.interpret(context);
  }
}

// 為 (x + 5) * y 建立 AST
const context: Context = new Map([
  ['x', 3],
  ['y', 4],
]);

const expression = new MultiplyExpression(
  new AddExpression(
    new VariableExpression('x'),
    new NumberExpression(5),
  ),
  new VariableExpression('y'),
);

console.log(expression.interpret(context)); // (3 + 5) * 4 = 32

// 只改 context,不必動表達式樹
context.set('x', 10);
console.log(expression.interpret(context)); // (10 + 5) * 4 = 60

適用情境

適用時機

  • 需要求值一種簡單語言,且該語言的句子可以表示成抽象語法樹
  • 設定用的 DSL、簡單的規則引擎、小型指令集

注意事項

  • 文法一旦變複雜,解釋器模式會導致類別數量爆炸。真正的語言應該改用 ANTLR 這類專門的剖析器產生器。
  • 在多數情況下,理解這個模式的設計思想,比直接拿來用更有價值。

總結

Interpreter 是把語言文法對應成物件樹的經典手法。

理解它,能讓你看懂編譯器與模板引擎背後的其中一層概念設 —— 使你從來不需要從頭手刻一個。