DPDesign Pattern Quest
パターン一覧/Interpreter
#15 / 23
B振る舞いDIFF

インタプリタ

Interpreter

ミニ言語の通訳係。検索フィルタやルール式を、文法のクラス階層に翻訳して評価する。

Intent · 目的

あるドメイン特化のミニ言語の文法を、クラス階層として表現する。各文法ルールが1クラスになり、構文木を組み立てて `interpret()` を呼ぶと評価が進む。

Motivation · 動機

「アクセス権限を `role==admin && plan==pro` みたいな式で書きたい」「検索フィルタに `tag:react -tag:legacy` を許したい」── 専用の言語を組み込みたい場面はある。文字列を毎回パーサで解析して `eval` してたら危険だし、構造化もされない。文法をクラス(数値、変数、AND、OR、NOT…)として表現すれば、AST を組み立てて木を辿るだけで評価できる。レゴでミニ言語を組み立てるイメージ。本格的な言語処理にはパーサジェネレータの方がいいが、軽い DSL なら自前で十分。

適用場面

  • 1規則がそれほど多くない自前 DSL(検索式、権限式、簡易計算式)。
  • 2文法が安定していて、頻繁にコロコロ変わらない場合。
  • 3実行効率より拡張性・可読性を優先したい場合。

概念図

サンプルコード

interface Expr { interpret(ctx: Record<string, number>): number; }

class Num implements Expr {
  constructor(private v: number) {}
  interpret(){ return this.v; }
}
class Var implements Expr {
  constructor(private n: string) {}
  interpret(ctx: Record<string, number>){ return ctx[this.n] ?? 0; }
}
class Add implements Expr {
  constructor(private l: Expr, private r: Expr) {}
  interpret(ctx: Record<string, number>){ return this.l.interpret(ctx) + this.r.interpret(ctx); }
}

const e: Expr = new Add(new Var("x"), new Num(3));
console.log(e.interpret({ x: 10 })); // 13
Pros · メリット
  • 新しい文法ルールはクラス1個追加すれば済む。拡張は局所的。
  • AST 操作が自然に書ける。文法の構造がそのままコードに現れる。
  • ルールが少なければ、自前パーサ+専用ライブラリより読みやすいことも。
Cons · デメリット
  • 規則が多くなるとクラス爆発する。30個ルールがあるDSLは、もう本パターン向きじゃない。
  • 実行効率は良くない。本格的な言語処理にはパーサジェネレータ+VMの方が桁違いに速い。
  • AST 設計の負荷が高く、メンテが特定の人しかできなくなる現象が起こりがち。

関連パターン

⚔ ready to test

理解度を確かめる時間だ

3 問のクイズで、 このパターンを身体に染み込ませよう。

挑戦する →