🧩 Interpreter Pattern
✅ Intent
- Represent conditional logic or business rules as a grammar, and define a structure for interpreting and evaluating it
- Ideal for building domain-specific languages (DSLs) for rule evaluation
✅ Motivation
- Complex and frequently changing conditional logic can be defined as an abstract syntax tree and delegated to an interpreter
- Transforms deeply nested
if
/switch
statements into a flexible, extensible structure
✅ When to Use
- Business rule engines, filter conditions, search expressions, etc.
- When rules are defined by non-engineers or via configuration files and evaluated at runtime
✅ Code Example
- 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
✅ Explanation
This code applies the Interpreter
pattern to represent discount rules as a grammar and evaluate them flexibly.
The Interpreter
pattern defines a grammar for a given language and provides an interpreter to evaluate expressions written in that language. It is particularly useful for complex or dynamic rule evaluation.
1. Overview of the Interpreter Pattern
-
Expression: Defines the common interface for all grammar expressions
- Represented by
Expression
in this code
- Represented by
-
TerminalExpression: Implements atomic expressions containing specific logic
- Represented by
DiscountExpression
- Represented by
-
NonTerminalExpression: Combines multiple expressions and defines how they are evaluated
- Represented by
DiscountInterpreter
- Represented by
-
Client: Constructs the grammar and executes the interpretation
- In this code, the part where multiple
DiscountExpression
s are added toDiscountInterpreter
andinterpret
is called
- In this code, the part where multiple
2. Key Classes and Their Roles
-
Expression
- Common interface for all expressions
- Declares the method
interpret(userType: string, basePrice: number): number
-
DiscountExpression
- A
TerminalExpression
- Applies a specific discount rate (
rate
) if the user type matches (targetType
)
- A
-
DiscountInterpreter
- A
NonTerminalExpression
- Holds multiple
DiscountExpression
instances and evaluates them in order - Returns the result of the first matching expression
- A
-
Client Code
- Adds multiple
DiscountExpression
s toDiscountInterpreter
and executes the evaluation usinginterpret
- Adds multiple
3. UML Class Diagram
4. Benefits of the Interpreter Pattern
- Flexible Rule Definition: Rules can be expressed as objects, enabling concise representation of complex conditions
- Extensibility: New rules can be added simply by creating a new
DiscountExpression
and adding it to the interpreter - Reusability: Each expression is independent and can be reused across different contexts
This design is highly effective for managing complex or dynamic rule sets, and is especially useful in scenarios where rules may evolve or grow over time.