KnowHow

技術的なメモを中心にまとめます。
検索にて調べることができます。

Python基礎 オブジェクト指向23(Interpreter) 振る舞いに関するデザインパターン

登録日 :2026/02/07 18:31
カテゴリ :Python基礎

(オブジェクトの種類)

構文の解析を行なって、その結果を利用して処理を行うパターン

  • 解析を行うための「規則」をクラスとして表現

  • 規則のツリー構造を扱うことができる

構成要素

Context

  • 解析を行いたい値や情報を提供するクラス

AbstractExpression

  • 規則を表す抽象クラスもしくはインターフェース

  • 規則が解析を行うための共通APIを定義

NonterminalExpression

  • AbstractExpressionを継承(実装)する子クラス

  • 具体的な規則を実装

  • 内部にAbstractExpression型のオブジェクトを保持することで、規則の親子関係を表現

TerminalExpression

  • AbstractExpressionを継承(実装)する子クラス

  • 具体的な規則を実装

  • 末端の規則を表す

オブジェクト指向的要素

  • ポリモーフィズムを利用したパターン

AbstractExpressionでクライアントに対してアクセスさせるための共通APIを定義

NonterminalExpressionは内部にAbstractExpression型のオブジェクトを保持

クライアントやNonterminalExpressionは、共通APIを使用することで具体的な実装を意識する必要がなくなる

TerminalExpressionやNonTerminalExpressioinの追加・削除・切り替え等が可能

メリット・デメリット

メリット

  • 既存のコードを修正することなく、規則の追加・拡張が可能

デメリット

  • シンプルな規則を実現する場合、コードがより複雑になる可能性がある

使い所

特定の文法で記述された内容を解析して処理したい場合

  • 正規表現

  • SQL解析

  • プログラミング言語開発

サンプル

例)日付型をフォーマットする例(YYYY-MM-DD)

from abc import ABC, abstractmethod
import datetime
import re


class Context(object):
    def __init__(self, expression: str, date: datetime.date):
        self.validate(expression)
        self.expression = expression
        self.date = date

    def validate(self, expression: str):
        if len(expression) != 10 or not bool(re.match(
            "(?=.*YYYY)(?=.*MM)(?=.*DD)", expression
        )):
            raise Exception("[ERROR] Wrong Expression.")


class AbstractExpression(ABC):
    @abstractmethod
    def interpret(self, context: Context):
        pass


class YearExpression(AbstractExpression):
    def __init__(self):
        self.__child = None

    def set_child(self, child: AbstractExpression):
        self.__child = child

    def interpret(self, context: Context):
        expression = context.expression
        year = context.date.year
        context.expression = expression.replace("YYYY", str(year))

        if self.__child:
            self.__child.interpret(context)

        return context


class MonthExpression(AbstractExpression):
    def __init__(self):
        self.__child = None

    def set_child(self, child: AbstractExpression):
        self.__child = child

    def interpret(self, context: Context):
        expression = context.expression
        month = context.date.month
        context.expression = expression.replace("MM", str(month).zfill(2))

        if self.__child:
            self.__child.interpret(context)

        return context


class DayExpression(AbstractExpression):
    def __init__(self):
        self.__child = None

    def set_child(self, child: AbstractExpression):
        self.__child = child

    def interpret(self, context: Context):
        expression = context.expression
        day = context.date.day
        context.expression = expression.replace("DD", str(day).zfill(2))

        if self.__child:
            self.__child.interpret(context)

        return context


if __name__ == '__main__':
    now_date = datetime.datetime.now().date()
    expression = "MM/DD/YYYY"

    context = Context(expression, now_date)

    year_expression = YearExpression()
    month_expression = MonthExpression()
    day_expression = DayExpression()

    month_expression.set_child(day_expression)
    year_expression.set_child(month_expression)

    result = year_expression.interpret(context)

    print(now_date.strftime("%m/%d/%Y"))
    print(result.expression)