0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

全30回:静的と動的でどう違うのか、JavaとPythonで学ぶデザインパターン - Day 17 Interpreterパターン:独自言語とDSLを構築する設計技法

Posted at

はじめに

皆さん、こんにちは!「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パターン:オブジェクトの状態に応じた振る舞いの動的変更」

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?