2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonにおける状態パターン:状態遷移をオブジェクトに委譲する設計

Posted at

概要

**状態パターン(State Pattern)**は、
オブジェクトの内部状態に応じて、その振る舞い(メソッドの実装)を動的に切り替える設計手法。

状態をクラスとして独立させることで、条件分岐の削減、状態遷移の明示、振る舞いの分離が可能となり、
複雑な状態管理を洗練された構造で実現できる。

本稿では、Pythonにおける状態パターンの実装と実務活用例を解説する。


1. なぜStateパターンが必要か?

❌ 状態に応じた if-else が膨張する構造

if self.state == "draft":
    self.save_as_draft()
elif self.state == "submitted":
    self.submit_to_approval()
elif self.state == "approved":
    self.lock()

→ 状態が増えるたびに分岐が複雑化、保守困難に


✅ 状態ごとの振る舞いをクラスに分離

class DraftState:
    def handle(self, context): ...

class ApprovedState:
    def handle(self, context): ...

状態をオブジェクトとして設計することで、処理がスケーラブルに


2. 基本構造

✅ 状態インタフェース

class State:
    def submit(self, context): raise NotImplementedError
    def approve(self, context): raise NotImplementedError

✅ 具体的状態クラス

class Draft(State):
    def submit(self, context):
        print("Submitted")
        context.set_state(Submitted())

    def approve(self, context):
        print("Cannot approve draft")

class Submitted(State):
    def submit(self, context):
        print("Already submitted")

    def approve(self, context):
        print("Approved")
        context.set_state(Approved())

class Approved(State):
    def submit(self, context):
        print("Already approved")

    def approve(self, context):
        print("Already approved")

✅ コンテキストクラス

class Document:
    def __init__(self):
        self.state = Draft()

    def set_state(self, state):
        self.state = state

    def submit(self):
        self.state.submit(self)

    def approve(self):
        self.state.approve(self)

✅ 実行例

doc = Document()
doc.submit()   # → Submitted
doc.approve()  # → Approved
doc.approve()  # → Already approved

3. 状態パターンのメリット

  • 状態ごとの処理が明確に分離されるため、可読性と拡張性が高い
  • 状態遷移のルールをオブジェクトレベルで管理できる
  • if-else地獄を排除し、振る舞いを構造としてモデル化できる

4. 実務的ユースケース

✅ ワークフロー管理

  • 「作成 → 提出 → 承認 → 完了」といった複雑な状態遷移を制御
  • 各状態における許可アクションをクラス内で定義し、フローの正当性を担保

✅ UIコンポーネントの状態制御

  • ボタン:enabled / hovered / clicked / disabled
  • 状態クラスを切り替えることで、描画や振る舞いを一貫管理

✅ ゲーム開発:プレイヤーのステート管理

  • 例:Idle, Running, Attacking, Dying
  • 各状態が enter(), exit(), update() を持ち、状態マシンの構築が容易

5. Python的に書きやすくする工夫

✅ EnumとStateクラスの併用

from enum import Enum

class Status(Enum):
    DRAFT = "draft"
    SUBMITTED = "submitted"
    APPROVED = "approved"

→ 状態の識別子をEnumで定義し、Stateインスタンスとマッピング


✅ 状態テーブルで動的切替(簡易型)

class StateMachine:
    def __init__(self):
        self.state = "draft"

    def transition(self, event):
        rules = {
            ("draft", "submit"): "submitted",
            ("submitted", "approve"): "approved"
        }
        self.state = rules.get((self.state, event), self.state)

→ 小規模なら状態テーブルで管理するパターンも選択肢に


6. よくある誤用と対策

❌ 状態を文字列や数値で持ち続ける

→ ✅ State クラスとして定義し、振る舞いと状態を結合させる


❌ 状態間の遷移ロジックをコンテキスト側に書く

→ ✅ 状態クラスが自身の遷移先を決定することで、流れが一元化される


結語

状態パターンは、「条件分岐による振る舞いの切替」を「構造による振る舞いの委譲」に変換する設計手法である。

  • 状態の数が増えるほど、構造化の恩恵が大きくなる
  • 分岐の除去、可視性の向上、状態遷移の明示という、実務的な設計課題に直結
  • Pythonのクラスベース設計との親和性も高く、OOPの理解深化にも繋がる

Pythonicとは、“条件を消し、構造で語る”ことであり、
状態パターンはその哲学を実現するための洗練された形式である。

2
3
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
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?