🧩 Interpreter パターン
✅ 設計意図
- ドメイン固有言語(DSL)のように、条件ロジックやルールを文法として表現し、解釈・評価できる構造を定義する
✅ 適用理由
- 複雑で頻繁に変更される条件分岐を、構文ツリーとして定義し、評価器(interpreter)に任せる
- if 文/switch 文がネストして保守困難になっているロジックを、柔軟に分離・追加できる形にする
✅ 向いているシーン
- ビジネスルールエンジン、フィルタ条件、検索条件式など
- ルールが非エンジニアや設定ファイルで記述され、実行時に評価される場合
✅ コード例
- TypeScript
- PHP
- Python
// 文法インターフェース
interface Expression {
interpret(userType: string, basePrice: number): number;
}
// ターミナル式(具体的な条件と計算)
class DiscountExpression implements Expression {
constructor(private targetType: string, private rate: number) {}
interpret(userType: string, basePrice: number): number {
return userType === this.targetType ? basePrice * this.rate : basePrice;
}
}
// 非終端式(ANDやORなどにも拡張可能)
class DiscountInterpreter {
private expressions: DiscountExpression[] = [];
add(expression: DiscountExpression) {
this.expressions.push(expression);
}
interpret(userType: string, basePrice: number): number {
for (const expr of this.expressions) {
const result = expr.interpret(userType, basePrice);
if (result !== basePrice) return result;
}
return basePrice;
}
}
// 利用例
const interpreter = new DiscountInterpreter();
interpreter.add(new DiscountExpression("student", 0.8));
interpreter.add(new DiscountExpression("member", 0.9));
interpreter.add(new DiscountExpression("vip", 0.7));
console.log(interpreter.interpret("vip", 1000)); // 700
console.log(interpreter.interpret("student", 1000)); // 800
console.log(interpreter.interpret("guest", 1000)); // 1000
<?php
interface Expression {
public function interpret(string $userType, float $basePrice): float;
}
class DiscountExpression implements Expression {
public function __construct(private string $targetType, private float $rate) {}
public function interpret(string $userType, float $basePrice): float {
return $userType === $this->targetType ? $basePrice * $this->rate : $basePrice;
}
}
class DiscountInterpreter {
private array $expressions = [];
public function add(DiscountExpression $expr): void {
$this->expressions[] = $expr;
}
public function interpret(string $userType, float $basePrice): float {
foreach ($this->expressions as $expr) {
$result = $expr->interpret($userType, $basePrice);
if ($result !== $basePrice) {
return $result;
}
}
return $basePrice;
}
}
// 利用例
$interpreter = new DiscountInterpreter();
$interpreter->add(new DiscountExpression("student", 0.8));
$interpreter->add(new DiscountExpression("member", 0.9));
$interpreter->add(new DiscountExpression("vip", 0.7));
echo $interpreter->interpret("vip", 1000) . PHP_EOL; // 700
echo $interpreter->interpret("student", 1000) . PHP_EOL; // 800
echo $interpreter->interpret("guest", 1000) . PHP_EOL; // 1000
from abc import ABC, abstractmethod
class Expression(ABC):
@abstractmethod
def interpret(self, user_type: str, base_price: float) -> float:
pass
class DiscountExpression(Expression):
def __init__(self, target_type: str, rate: float):
self.target_type = target_type
self.rate = rate
def interpret(self, user_type: str, base_price: float) -> float:
return base_price * self.rate if user_type == self.target_type else base_price
class DiscountInterpreter:
def __init__(self):
self.expressions: list[DiscountExpression] = []
def add(self, expr: DiscountExpression):
self.expressions.append(expr)
def interpret(self, user_type: str, base_price: float) -> float:
for expr in self.expressions:
result = expr.interpret(user_type, base_price)
if result != base_price:
return result
return base_price
# 利用例
interpreter = DiscountInterpreter()
interpreter.add(DiscountExpression("student", 0.8))
interpreter.add(DiscountExpression("member", 0.9))
interpreter.add(DiscountExpression("vip", 0.7))
print(interpreter.interpret("vip", 1000)) # 700
print(interpreter.interpret("student", 1000)) # 800
print(interpreter.interpret("guest", 1000)) # 1000
✅ 解説
このコードは Interpreter
パターン を使用して、割引計算のルールを文法として表現し、柔軟に評価できる設計を実現している。
Interpreter
パターンは、特定の文法を定義し、その文法に従った式を評価するデザインパターンであり、特に複雑なルールや条件を扱う場合に有効。
1. Interpreter
パターンの概要
- Expression: 文法の共通インターフェースを定義
- このコードでは
Expression
が該当
- このコードでは
- TerminalExpression: 文法の末端要素を表現し、具体的な条件や計算を実装
- このコードでは
DiscountExpression
が該当
- このコードでは
- NonTerminalExpression: 文法の非終端要素を表現し、複数の式を組み合わせて評価
- このコードでは
DiscountInterpreter
が該当
- このコードでは
- Client: 文法を構築し、評価を実行
- このコードでは
DiscountInterpreter
に式を追加し、interpret
メソッドを呼び出す部分が該当
- このコードでは
2. 主なクラスとその役割
Expression
- 文法の共通インターフェース
interpret(userType: string, basePrice: number): number
メソッドを定義
DiscountExpression
TerminalExpression
(ターミナル式)- 特定のユーザータイプ(
targetType
)に対して割引率(rate
)を適用するロジックを実装
DiscountInterpreter
NonTerminalExpression
(非終端式)- 複数の
DiscountExpression
を保持し、順に評価 - 最初に一致する式が見つかった場合、その結果を返す
- クライアントコード
DiscountInterpreter
に複数のDiscountExpression
を追加し、interpret
メソッドで評価を実行
3. UML クラス図
4. Interpreter パターンの利点
- 柔軟なルール定義: 文法をオブジェクトとして表現することで、複雑なルールを簡潔に定義可能。
- 拡張性: 新しいルールを追加する場合も、
DiscountExpression
を作成してDiscountInterpreter
に追加するだけで対応可能。 - 再利用性: 各式(
DiscountExpression
)が独立しているため、他の文脈でも再利用可能。
この設計は、複雑な条件やルールを扱う際に非常に有効であり、特に動的にルールを変更したい場合や、条件が増える可能性がある場面で有効に機能する。