はじめに
皆さん、こんにちは!「JavaとPythonで比べるデザインパターン」シリーズの第17回目です。
今回は、独自の文法やドメイン固有言語(DSL)を構築し、それを解釈・実行するためのInterpreter(インタープリター)パターンについて詳しく解説します。
Interpreterパターンとは?
Interpreterパターンは、特定の文法規則を持つ言語を定義し、その文を解釈・実行する振る舞いパターンです。このパターンでは、文法の各ルールをクラスで表現し、抽象構文木(AST: Abstract Syntax Tree)を構築して、再帰的に文を解釈します。
身近な例:スマート家電の音声コマンド解釈システム
現代のスマート家電では、自然言語に近い音声コマンドを解釈する機能があります:
音声コマンドの例
"リビングの照明を50%にして、温度を22度に設定"
↓ 解釈・分解
├── "リビングの照明を50%にして" → SetBrightnessCommand(room="living", value=50)
└── "温度を22度に設定" → SetTemperatureCommand(value=22)
従来の固定コマンド方式
switch(command) {
case "LIGHT_ON": turnOnLight(); break;
case "LIGHT_OFF": turnOffLight(); break;
case "TEMP_UP": increaseTemp(); break;
// 新しいコマンド追加時にはコード修正が必要
}
Interpreterパターン適用後
Command Grammar Definition:
SetCommand := "set" + Device + "to" + Value
Device := "light" | "temperature" | "volume" | ...
Value := Number | Percentage | State
新しいコマンドは文法定義の追加のみで対応可能
Interpreterパターンの核心価値
1. 文法とロジックの分離
- 文法定義: 言語の構造を抽象構文木として表現
- 解釈ロジック: 各ノードが自身の解釈方法を知っている
- 実行エンジン: 構文木を走査して処理を実行
2. 拡張性の確保
- 新しい文法ルールの追加が既存コードに影響しない
- コンポジットパターンとの組み合わせで柔軟な構造
- 再帰的な処理により複雑な式も単純な構造で表現
3. ドメイン固有言語(DSL)の構築
- ビジネスルールを自然な形で表現
- 非プログラマーでも理解しやすい記法
- 設定ファイルやクエリ言語の実装基盤
抽象構文木(AST)の構造理解
Expression: (x + 5) * (y - 2)
MultiplyExpression
/ \
AddExpression SubtractExpression
/ \ / \
Variable(x) Number(5) Variable(y) Number(2)
各ノードがinterpret()メソッドを持ち、子ノードの結果を使って自身の計算を行います。
Javaでの実装:型安全性と厳密な文法定義
Javaでは、インターフェースと抽象クラスを活用して、厳密で拡張可能な言語処理システムを構築できます。
実装例:ビジネスルール評価エンジン
import java.util.*;
import java.util.function.Function;
// 抽象式インターフェース
interface Expression {
Object interpret(Context context);
String toString();
}
// コンテキスト(実行時の状態管理)
class Context {
private final Map<String, Object> variables = new HashMap<>();
private final Map<String, Function<Object[], Object>> functions = new HashMap<>();
public void setVariable(String name, Object value) {
variables.put(name, value);
}
public Object getVariable(String name) {
return variables.get(name);
}
public void defineFunction(String name, Function<Object[], Object> function) {
functions.put(name, function);
}
public Object callFunction(String name, Object... args) {
Function<Object[], Object> function = functions.get(name);
if (function != null) {
return function.apply(args);
}
throw new RuntimeException("Undefined function: " + name);
}
public boolean hasVariable(String name) {
return variables.containsKey(name);
}
}
// 終端式:数値リテラル
class NumberExpression implements Expression {
private final double value;
public NumberExpression(double value) {
this.value = value;
}
@Override
public Object interpret(Context context) {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
// 終端式:文字列リテラル
class StringExpression implements Expression {
private final String value;
public StringExpression(String value) {
this.value = value;
}
@Override
public Object interpret(Context context) {
return value;
}
@Override
public String toString() {
return "\"" + value + "\"";
}
}
// 終端式:変数参照
class VariableExpression implements Expression {
private final String name;
public VariableExpression(String name) {
this.name = name;
}
@Override
public Object interpret(Context context) {
if (context.hasVariable(name)) {
return context.getVariable(name);
}
throw new RuntimeException("Undefined variable: " + name);
}
@Override
public String toString() {
return name;
}
}
// 抽象基底クラス:二項演算
abstract class BinaryExpression implements Expression {
protected final Expression left;
protected final Expression right;
public BinaryExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
protected abstract Object operate(Object leftValue, Object rightValue);
protected abstract String getOperator();
@Override
public Object interpret(Context context) {
Object leftValue = left.interpret(context);
Object rightValue = right.interpret(context);
return operate(leftValue, rightValue);
}
@Override
public String toString() {
return "(" + left.toString() + " " + getOperator() + " " + right.toString() + ")";
}
}
// 具象二項演算:加算
class AddExpression extends BinaryExpression {
public AddExpression(Expression left, Expression right) {
super(left, right);
}
@Override
protected Object operate(Object leftValue, Object rightValue) {
if (leftValue instanceof Number && rightValue instanceof Number) {
return ((Number) leftValue).doubleValue() + ((Number) rightValue).doubleValue();
}
// 文字列結合もサポート
return leftValue.toString() + rightValue.toString();
}
@Override
protected String getOperator() {
return "+";
}
}
// 具象二項演算:減算
class SubtractExpression extends BinaryExpression {
public SubtractExpression(Expression left, Expression right) {
super(left, right);
}
@Override
protected Object operate(Object leftValue, Object rightValue) {
if (leftValue instanceof Number && rightValue instanceof Number) {
return ((Number) leftValue).doubleValue() - ((Number) rightValue).doubleValue();
}
throw new RuntimeException("Subtraction requires numeric operands");
}
@Override
protected String getOperator() {
return "-";
}
}
// 具象二項演算:乗算
class MultiplyExpression extends BinaryExpression {
public MultiplyExpression(Expression left, Expression right) {
super(left, right);
}
@Override
protected Object operate(Object leftValue, Object rightValue) {
if (leftValue instanceof Number && rightValue instanceof Number) {
return ((Number) leftValue).doubleValue() * ((Number) rightValue).doubleValue();
}
throw new RuntimeException("Multiplication requires numeric operands");
}
@Override
protected String getOperator() {
return "*";
}
}
// 具象二項演算:比較
class GreaterThanExpression extends BinaryExpression {
public GreaterThanExpression(Expression left, Expression right) {
super(left, right);
}
@Override
protected Object operate(Object leftValue, Object rightValue) {
if (leftValue instanceof Number && rightValue instanceof Number) {
return ((Number) leftValue).doubleValue() > ((Number) rightValue).doubleValue();
}
throw new RuntimeException("Comparison requires numeric operands");
}
@Override
protected String getOperator() {
return ">";
}
}
// 条件式(三項演算子)
class ConditionalExpression implements Expression {
private final Expression condition;
private final Expression trueExpression;
private final Expression falseExpression;
public ConditionalExpression(Expression condition, Expression trueExpression, Expression falseExpression) {
this.condition = condition;
this.trueExpression = trueExpression;
this.falseExpression = falseExpression;
}
@Override
public Object interpret(Context context) {
Object conditionValue = condition.interpret(context);
boolean isTrue = false;
if (conditionValue instanceof Boolean) {
isTrue = (Boolean) conditionValue;
} else if (conditionValue instanceof Number) {
isTrue = ((Number) conditionValue).doubleValue() != 0;
}
return isTrue ? trueExpression.interpret(context) : falseExpression.interpret(context);
}
@Override
public String toString() {
return condition + " ? " + trueExpression + " : " + falseExpression;
}
}
// 関数呼び出し式
class FunctionCallExpression implements Expression {
private final String functionName;
private final List<Expression> arguments;
public FunctionCallExpression(String functionName, Expression... arguments) {
this.functionName = functionName;
this.arguments = Arrays.asList(arguments);
}
@Override
public Object interpret(Context context) {
Object[] argValues = arguments.stream()
.map(arg -> arg.interpret(context))
.toArray();
return context.callFunction(functionName, argValues);
}
@Override
public String toString() {
return functionName + "(" +
arguments.stream().map(Object::toString).reduce((a, b) -> a + ", " + b).orElse("") +
")";
}
}
// 使用例とテストケース
public class BusinessRuleEngine {
public static void main(String[] args) {
Context context = new Context();
// 変数の設定
context.setVariable("customerAge", 25);
context.setVariable("purchaseAmount", 1000.0);
context.setVariable("customerType", "PREMIUM");
// 関数の定義
context.defineFunction("max", args -> {
double max = Double.NEGATIVE_INFINITY;
for (Object arg : args) {
if (arg instanceof Number) {
max = Math.max(max, ((Number) arg).doubleValue());
}
}
return max;
});
context.defineFunction("discount", args -> {
if (args.length >= 2 && args[0] instanceof Number && args[1] instanceof String) {
double amount = ((Number) args[0]).doubleValue();
String type = (String) args[1];
switch (type) {
case "PREMIUM": return amount * 0.1;
case "STANDARD": return amount * 0.05;
default: return 0.0;
}
}
return 0.0;
});
// 複雑なビジネスルールの構築
// "年齢が18歳以上で、購入額が500以上の場合、割引を適用"
Expression ageCheck = new GreaterThanExpression(
new VariableExpression("customerAge"),
new NumberExpression(18)
);
Expression amountCheck = new GreaterThanExpression(
new VariableExpression("purchaseAmount"),
new NumberExpression(500)
);
Expression discountCalculation = new FunctionCallExpression(
"discount",
new VariableExpression("purchaseAmount"),
new VariableExpression("customerType")
);
Expression finalDiscount = new ConditionalExpression(
new MultiplyExpression(ageCheck, amountCheck), // AND演算(数値的)
discountCalculation,
new NumberExpression(0)
);
// ルールの実行
System.out.println("Business Rule: " + finalDiscount);
System.out.println("Result: " + finalDiscount.interpret(context));
// より複雑な例:動的価格計算
Expression dynamicPrice = new AddExpression(
new VariableExpression("purchaseAmount"),
new SubtractExpression(
new FunctionCallExpression("max",
new NumberExpression(50),
new MultiplyExpression(new VariableExpression("purchaseAmount"), new NumberExpression(0.02))
),
discountCalculation
)
);
System.out.println("Dynamic Price Calculation: " + dynamicPrice);
System.out.println("Final Price: " + dynamicPrice.interpret(context));
}
}
Javaの特徴と利点
- 型安全性: コンパイル時に文法違反を検出
- 拡張性: 新しい演算子や関数を簡単に追加
- デバッグ支援: スタックトレースによる詳細なエラー情報
- パフォーマンス: JIT コンパイルによる最適化
Pythonでの実装:動的性と柔軟性を活かす
Pythonでは、動的型付けの特性とメタプログラミング機能を活用して、より柔軟で表現力豊かな言語処理システムを構築できます。
from abc import ABC, abstractmethod
from typing import Any, Dict, Callable, List, Union
import operator
import re
# 抽象基底クラス
class Expression(ABC):
@abstractmethod
def interpret(self, context: 'Context') -> Any:
pass
@abstractmethod
def __str__(self) -> str:
pass
# コンテキスト(実行時状態管理)
class Context:
def __init__(self):
self.variables: Dict[str, Any] = {}
self.functions: Dict[str, Callable] = {}
# 組み込み関数の定義
self.define_function('max', max)
self.define_function('min', min)
self.define_function('abs', abs)
self.define_function('round', round)
def set_variable(self, name: str, value: Any) -> None:
self.variables[name] = value
def get_variable(self, name: str) -> Any:
if name in self.variables:
return self.variables[name]
raise NameError(f"Variable '{name}' is not defined")
def define_function(self, name: str, func: Callable) -> None:
self.functions[name] = func
def call_function(self, name: str, *args) -> Any:
if name in self.functions:
return self.functions[name](*args)
raise NameError(f"Function '{name}' is not defined")
def has_variable(self, name: str) -> bool:
return name in self.variables
# 終端式:数値
class NumberExpression(Expression):
def __init__(self, value: Union[int, float]):
self.value = value
def interpret(self, context: Context) -> Union[int, float]:
return self.value
def __str__(self) -> str:
return str(self.value)
# 終端式:文字列
class StringExpression(Expression):
def __init__(self, value: str):
self.value = value
def interpret(self, context: Context) -> str:
return self.value
def __str__(self) -> str:
return f'"{self.value}"'
# 終端式:変数参照
class VariableExpression(Expression):
def __init__(self, name: str):
self.name = name
def interpret(self, context: Context) -> Any:
return context.get_variable(self.name)
def __str__(self) -> str:
return self.name
# 二項演算の基底クラス
class BinaryExpression(Expression):
def __init__(self, left: Expression, right: Expression):
self.left = left
self.right = right
@abstractmethod
def operate(self, left_value: Any, right_value: Any) -> Any:
pass
@abstractmethod
def get_operator(self) -> str:
pass
def interpret(self, context: Context) -> Any:
left_value = self.left.interpret(context)
right_value = self.right.interpret(context)
return self.operate(left_value, right_value)
def __str__(self) -> str:
return f"({self.left} {self.get_operator()} {self.right})"
# 具象二項演算(演算子モジュールを活用)
class AddExpression(BinaryExpression):
def operate(self, left_value: Any, right_value: Any) -> Any:
return operator.add(left_value, right_value)
def get_operator(self) -> str:
return "+"
class SubtractExpression(BinaryExpression):
def operate(self, left_value: Any, right_value: Any) -> Any:
return operator.sub(left_value, right_value)
def get_operator(self) -> str:
return "-"
class MultiplyExpression(BinaryExpression):
def operate(self, left_value: Any, right_value: Any) -> Any:
return operator.mul(left_value, right_value)
def get_operator(self) -> str:
return "*"
class DivideExpression(BinaryExpression):
def operate(self, left_value: Any, right_value: Any) -> Any:
if right_value == 0:
raise ZeroDivisionError("Division by zero")
return operator.truediv(left_value, right_value)
def get_operator(self) -> str:
return "/"
class GreaterThanExpression(BinaryExpression):
def operate(self, left_value: Any, right_value: Any) -> bool:
return operator.gt(left_value, right_value)
def get_operator(self) -> str:
return ">"
class LogicalAndExpression(BinaryExpression):
def operate(self, left_value: Any, right_value: Any) -> bool:
return bool(left_value) and bool(right_value)
def get_operator(self) -> str:
return "and"
# 条件式
class ConditionalExpression(Expression):
def __init__(self, condition: Expression, true_expr: Expression, false_expr: Expression):
self.condition = condition
self.true_expr = true_expr
self.false_expr = false_expr
def interpret(self, context: Context) -> Any:
condition_value = self.condition.interpret(context)
if bool(condition_value):
return self.true_expr.interpret(context)
else:
return self.false_expr.interpret(context)
def __str__(self) -> str:
return f"{self.condition} ? {self.true_expr} : {self.false_expr}"
# 関数呼び出し
class FunctionCallExpression(Expression):
def __init__(self, func_name: str, *args: Expression):
self.func_name = func_name
self.args = args
def interpret(self, context: Context) -> Any:
arg_values = [arg.interpret(context) for arg in self.args]
return context.call_function(self.func_name, *arg_values)
def __str__(self) -> str:
args_str = ", ".join(str(arg) for arg in self.args)
return f"{self.func_name}({args_str})"
# リスト式(Pythonらしい機能)
class ListExpression(Expression):
def __init__(self, *elements: Expression):
self.elements = elements
def interpret(self, context: Context) -> List[Any]:
return [element.interpret(context) for element in self.elements]
def __str__(self) -> str:
elements_str = ", ".join(str(element) for element in self.elements)
return f"[{elements_str}]"
# インデックスアクセス
class IndexExpression(Expression):
def __init__(self, collection: Expression, index: Expression):
self.collection = collection
self.index = index
def interpret(self, context: Context) -> Any:
collection_value = self.collection.interpret(context)
index_value = self.index.interpret(context)
return collection_value[index_value]
def __str__(self) -> str:
return f"{self.collection}[{self.index}]"
# 簡易パーサー(DSLの構文解析)
class SimpleParser:
def __init__(self, expression_string: str):
self.tokens = self._tokenize(expression_string)
self.pos = 0
def _tokenize(self, expr: str) -> List[str]:
# 簡単なトークナイザー(実用的にはより複雑なパーサーが必要)
pattern = r'(\d+\.?\d*|[a-zA-Z_]\w*|[+\-*/()><=]|"[^"]*")'
return re.findall(pattern, expr.replace(' ', ''))
def parse(self) -> Expression:
return self._parse_expression()
def _parse_expression(self) -> Expression:
left = self._parse_term()
while self.pos < len(self.tokens) and self.tokens[self.pos] in ['+', '-']:
op = self.tokens[self.pos]
self.pos += 1
right = self._parse_term()
if op == '+':
left = AddExpression(left, right)
else:
left = SubtractExpression(left, right)
return left
def _parse_term(self) -> Expression:
left = self._parse_factor()
while self.pos < len(self.tokens) and self.tokens[self.pos] in ['*', '/']:
op = self.tokens[self.pos]
self.pos += 1
right = self._parse_factor()
if op == '*':
left = MultiplyExpression(left, right)
else:
left = DivideExpression(left, right)
return left
def _parse_factor(self) -> Expression:
if self.pos >= len(self.tokens):
raise SyntaxError("Unexpected end of expression")
token = self.tokens[self.pos]
self.pos += 1
if token == '(':
expr = self._parse_expression()
if self.pos < len(self.tokens) and self.tokens[self.pos] == ')':
self.pos += 1
return expr
else:
raise SyntaxError("Missing closing parenthesis")
elif token.replace('.', '').isdigit():
return NumberExpression(float(token) if '.' in token else int(token))
elif token.startswith('"') and token.endswith('"'):
return StringExpression(token[1:-1])
else:
return VariableExpression(token)
# 使用例
def main():
context = Context()
# 変数設定
context.set_variable('x', 10)
context.set_variable('y', 5)
context.set_variable('price', 100)
context.set_variable('discount_rate', 0.1)
# カスタム関数の定義
def calculate_discount(amount, rate):
return amount * rate
context.define_function('discount', calculate_discount)
# 手動でのAST構築
print("=== Manual AST Construction ===")
# 式: (x + y) * 2 - discount(price, discount_rate)
expression = SubtractExpression(
MultiplyExpression(
AddExpression(VariableExpression('x'), VariableExpression('y')),
NumberExpression(2)
),
FunctionCallExpression('discount', VariableExpression('price'), VariableExpression('discount_rate'))
)
print(f"Expression: {expression}")
print(f"Result: {expression.interpret(context)}")
# 条件付きロジック
conditional_expr = ConditionalExpression(
GreaterThanExpression(VariableExpression('x'), NumberExpression(5)),
StringExpression("x is greater than 5"),
StringExpression("x is not greater than 5")
)
print(f"Conditional: {conditional_expr}")
print(f"Result: {conditional_expr.interpret(context)}")
# リストとインデックス操作
print("\n=== List Operations ===")
context.set_variable('numbers', [1, 2, 3, 4, 5])
list_expr = ListExpression(
NumberExpression(1),
AddExpression(NumberExpression(2), NumberExpression(3)),
VariableExpression('x')
)
print(f"List: {list_expr}")
print(f"Result: {list_expr.interpret(context)}")
# 簡易パーサーのテスト
print("\n=== Simple Parser Test ===")
parser_expr = "x + y * 2"
parser = SimpleParser(parser_expr)
parsed_ast = parser.parse()
print(f"Parsed: {parser_expr} -> {parsed_ast}")
print(f"Result: {parsed_ast.interpret(context)}")
if __name__ == "__main__":
main()
Pythonの特徴と利点
- 動的性: 実行時の型変換と柔軟な処理
- 簡潔性: Duck Typingによるインターフェース不要
- 表現力: リスト内包表記、ラムダ式など豊富な構文
- メタプログラミング: 動的なクラス・関数生成
実践的な応用場面
1. ビジネスルールエンジン
// 保険料計算ルール
Expression premiumCalculation = new ConditionalExpression(
new GreaterThanExpression(new VariableExpression("age"), new NumberExpression(65)),
new MultiplyExpression(new VariableExpression("basePremium"), new NumberExpression(1.5)),
new VariableExpression("basePremium")
);
2. SQL風のクエリ言語
# SELECT name, age FROM users WHERE age > 18
query = SelectExpression(
["name", "age"],
FromExpression("users"),
WhereExpression(GreaterThanExpression(VariableExpression("age"), NumberExpression(18)))
)
3. 設定ファイルの動的評価
# config.rule: "if environment == 'production' then 'prod-server' else 'dev-server'"
config_rule = ConditionalExpression(
EqualExpression(VariableExpression("environment"), StringExpression("production")),
StringExpression("prod-server"),
StringExpression("dev-server")
)
4. 数式計算エンジン
// 科学計算: sin(pi * x / 180) + cos(pi * y / 180)
Expression trigExpression = new AddExpression(
new FunctionCallExpression("sin",
new DivideExpression(
new MultiplyExpression(new VariableExpression("pi"), new VariableExpression("x")),
new NumberExpression(180)
)
),
new FunctionCallExpression("cos",
new DivideExpression(
new MultiplyExpression(new VariableExpression("pi"), new VariableExpression("y")),
new NumberExpression(180)
)
)
);
パフォーマンス考慮事項
最適化戦略
Java
- AST キャッシュ: 同じ式の再利用
- コンパイル済み実行: バイトコード生成
- 型特化: プリミティブ型専用の演算子クラス
Python
-
__slots__使用: メモリ使用量削減 - キャッシュデコレータ: 計算結果のメモ化
- NumPy 統合: 数値計算の高速化
from functools import lru_cache
class OptimizedNumberExpression(Expression):
__slots__ = ['value']
def __init__(self, value: Union[int, float]):
self.value = value
@lru_cache(maxsize=128)
def interpret(self, context: Context) -> Union[int, float]:
return self.value
メモリ使用量の比較
# 標準実装
import sys
standard_expr = NumberExpression(42)
print(f"Standard expression size: {sys.getsizeof(standard_expr)} bytes")
# 最適化実装
optimized_expr = OptimizedNumberExpression(42)
print(f"Optimized expression size: {sys.getsizeof(optimized_expr)} bytes")
実装パターンの比較
| 特性 | Java | Python |
|---|---|---|
| 型安全性 | コンパイル時型チェック | 実行時型チェック(型ヒント推奨) |
| 拡張性 | インターフェース継承 | ABC継承またはDuck Typing |
| パフォーマンス | JIT最適化、静的型付け | インタープリター、動的型付け |
| デバッグ性 | スタックトレース詳細 | 動的エラー情報 |
| 文法表現力 | 厳密なクラス階層 | 柔軟なオブジェクト構造 |
| DSL構築 | 型安全なDSL | 自然言語に近いDSL |
実際のプロダクトでの活用例
1. Spring Expression Language (SpEL) - Java
// Spring FrameworkのSpEL実装例
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name == 'Nikola Tesla'");
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
boolean result = exp.getValue(context, tesla, Boolean.class);
2. Django Template Engine - Python
# Djangoテンプレートエンジンの変数展開
# {{ user.name|upper }} のような構文を内部的にAST化
class VariableNode:
def __init__(self, filter_expression):
self.filter_expression = filter_expression
def render(self, context):
return self.filter_expression.resolve(context)
3. Apache Spark SQL - Scala/Java
// Spark SQLのCatalyst Optimizerは大規模なAST処理
case class Add(left: Expression, right: Expression) extends BinaryArithmetic {
override def symbol: String = "+"
override def eval(input: InternalRow): Any = numeric.plus(left.eval(input), right.eval(input))
}
アンチパターンと注意点
1. 過度な複雑化
// BAD: 単純な条件分岐をわざわざInterpreterパターンで実装
if (user.getAge() > 18) {
return "adult";
} else {
return "minor";
}
// GOOD: 複雑なビジネスルールでのみ使用
Expression ageRule = parseBusinessRule("age > 18 AND (income > 50000 OR education == 'university')");
2. パフォーマンス無視
# BAD: 毎回新しいASTを構築
def calculate_daily(data):
for item in data:
ast = build_ast("price * quantity * tax_rate") # 非効率
result = ast.interpret(create_context(item))
# GOOD: ASTを再利用
calculation_ast = build_ast("price * quantity * tax_rate") # 一度だけ構築
def calculate_daily(data):
for item in data:
result = calculation_ast.interpret(create_context(item))
3. エラーハンドリングの不備
// BAD: エラー情報が不明確
public Object interpret(Context context) {
return left.interpret(context) + right.interpret(context); // ClassCastException の可能性
}
// GOOD: 詳細なエラー情報
public Object interpret(Context context) {
try {
Object leftValue = left.interpret(context);
Object rightValue = right.interpret(context);
if (!(leftValue instanceof Number) || !(rightValue instanceof Number)) {
throw new InterpreterException(
String.format("Addition requires numeric operands, got %s and %s at %s",
leftValue.getClass().getSimpleName(),
rightValue.getClass().getSimpleName(),
this.getSourceLocation())
);
}
return ((Number) leftValue).doubleValue() + ((Number) rightValue).doubleValue();
} catch (Exception e) {
throw new InterpreterException("Error evaluating addition expression: " + this, e);
}
}
次世代の発展: ANTLR と現代的なパーサー
ANTLR(Java/Python対応)
// ANTLR4を使用した本格的なパーサー生成
grammar Calculator;
expression
: expression ('*'|'/') expression # MulDiv
| expression ('+'|'-') expression # AddSub
| '(' expression ')' # Parens
| NUMBER # Number
| VARIABLE # Variable
;
NUMBER: [0-9]+('.'[0-9]+)?;
VARIABLE: [a-zA-Z][a-zA-Z0-9]*;
WS: [ \t\r\n]+ -> skip;
PlyPlus(Python専用)
from plyplus import Grammar
# 文法定義
grammar = Grammar("""
start: expr;
expr: expr '+' term | expr '-' term | term;
term: term '*' factor | term '/' factor | factor;
factor: '(' expr ')' | number | variable;
number: /\d+(\.\d+)?/;
variable: /[a-zA-Z][a-zA-Z0-9]*/;
WS: /\s+/ (%ignore);
""")
まとめ:適用場面の見極めが重要
Interpreterパターンは強力なパターンですが、**「言語的な構造を持つ問題」**に限定して使用することが重要です。
適用すべき場面
- 複雑なビジネスルールの動的評価
- **DSL(ドメイン固有言語)**の実装
- 設定ファイルの高度な条件評価
- クエリエンジンやレポート生成システム
- 数式処理や科学計算システム
避けるべき場面
- 単純な条件分岐(if-else で十分)
- 固定的な処理フロー
- パフォーマンスが最重要な処理
- 少数の固定パターンしか存在しない場合
| 特性 | Java | Python |
|---|---|---|
| 主な強み | 型安全性、大規模システム向け | 柔軟性、プロトタイピング向け |
| 適用領域 | エンタープライズ、金融システム | データ分析、機械学習パイプライン |
| パフォーマンス | 高速(JIT最適化) | 中程度(CPython制約) |
| 保守性 | 厳密な型による安全性 | 動的性による柔軟性 |
Interpreterパターンは、**「プログラムでプログラムを作る」**メタプログラミングの一形態です。適切に使用することで、システムの表現力と拡張性を大幅に向上させることができます。
次回は、オブジェクトの状態変化に応じて振る舞いを動的に変更するStateパターンについて解説します。ステートマシンの実装とビジネスワークフローの管理について詳しく学びましょう!
次回のテーマ:「Day 18 Stateパターン:オブジェクトの状態に応じた振る舞いの動的変更」