Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

振る舞いに関するパターン(Behavioral Patterns)

オブジェクト間の責務の分配やコミュニケーションの方法に関するパターン群。

Observer

状態が変わったら通知(イベントリスナー)

class Subject:
    def __init__(self):
        self.observers = []

    def attach(self, observer):
        self.observers.append(observer)

    def notify(self, msg):
        for ob in self.observers:
            ob.update(msg)

class Observer:
    def update(self, msg):
        print("Got:", msg)

s = Subject()
s.attach(Observer())
s.notify("Update available")
Got: Update available
例:logging

logging パッケージでは addHandler()self.handlers にハンドラーを追加し、ログ出力時に for hdlr in self.handlers で扱っていく

import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# 標準出力のハンドラーを追加
ch = logging.StreamHandler()
logger.addHandler(ch)

logger.info('hello world')

Lib/logging/init.py#L1734-L1737

Strategy

アルゴリズムを切り替えられるようにする

class Strategy:
    def execute(self, data):
        pass

class Add(Strategy):
    def execute(self, data):
        return sum(data)

class Multiply(Strategy):
    def execute(self, data):
        result = 1
        for x in data: result *= x
        return result

def run(strategy: Strategy, data):
    return strategy.execute(data)

print(run(Add(), [1, 2, 3]))
print(run(Multiply(), [1, 2, 3]))
6
6

State

オブジェクトの内部状態に応じて振る舞いを切り替えるパターン。if/switch による状態分岐の乱立を、状態ごとのクラスに置き換える。 Strategyと構造は似ているが、Stateはオブジェクト自身が状態遷移を管理する点が異なる。

class State:
    def handle(self, order): raise NotImplementedError

class PendingState(State):
    def handle(self, order):
        print("支払い待ち → 発送準備へ")
        order.state = ShippedState()

class ShippedState(State):
    def handle(self, order):
        print("発送済み → これ以上遷移しない")

class Order:
    def __init__(self):
        self.state: State = PendingState()

    def proceed(self):
        self.state.handle(self)

order = Order()
order.proceed()

Command

「実行してほしい操作」をオブジェクトとしてカプセル化するパターン。呼び出し元(Invoker)と実行対象(Receiver)を分離でき、 コマンドのキューイング・ログ記録・Undo/Redoの実装が容易になる。

class Light:
    def on(self): print("light on")
    def off(self): print("light off")

class Command:
    def execute(self): raise NotImplementedError

class LightOnCommand(Command):
    def __init__(self, light):
        self.light = light

    def execute(self):
        self.light.on()

class RemoteControl:
    def __init__(self):
        self.history = []

    def submit(self, command: Command):
        command.execute()
        self.history.append(command)

remote = RemoteControl()
remote.submit(LightOnCommand(Light()))

Iterator

集約オブジェクトの内部表現を公開せずに、要素へ順次アクセスする手段を提供するパターン。Pythonでは __iter__ / __next__ プロトコルとしてすでに言語仕様に組み込まれており、独自コンテナ型を作るときに自然と使うことになる。

class Range3:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        self.i = 0
        return self

    def __next__(self):
        if self.i >= self.n:
            raise StopIteration
        self.i += 1
        return self.i - 1

for x in Range3(3):
    print(x)