Stateパターン

概要

状態によってオブジェクトの挙動を変えるパターン」パターンです。

if文でも同じことはできますが、委譲を使用することで、挙動の変更がやりやすくなっています。

例題

以下の関数

  • 傘を持っていくか(unmbrella関数)
  • 洗濯物はどうするか(laundry関数) に対して、State(晴れ、くもり、雨)に応じて挙動を変えます。

# 傘を持っていくか

state行動プログラムとしての動き
晴れ傘は持っていかない"bring no umbrella"を出力
くもり折り畳み傘を持っていく"bring folding umbrella"を出力
傘を持っていく"bring normal umbrella"を出力

# 洗濯物はどうするか

state行動プログラムとしての動き
晴れ天日干し"dry outdoor"を出力
くもり部屋干し"dry indoor"を出力
部屋干し"dry indoor"を出力

サンプルコード

def main():
    obj = Context()
    obj.umbrella()
    obj.laundry()

    print("---")

    obj.change_state("rain")
    obj.umbrella()
    obj.laundry()


class State():
    def umbrella(self):
        raise NotImplementedError("umbrella is abstractmethod")

    def laundry(self):
        raise NotImplementedError("umbrella is abstractmethod")


class Sunnyday(State):
    def umbrella(self):
        print("bring no umbrella")

    def laundry(self):
        print("dry outdoor")


class Cloudyday(State):
    def umbrella(self):
        print("bring folding umbrella")

    def laundry(self):
        print("dry indoor")


class Rainyday(State):
    def umbrella(self):
        print("bring normal umbrella")

    def laundry(self):
        print("dry indoor")


class Context:
    def __init__(self):
        self.sunny = Sunnyday()
        self.rainy = Rainyday()
        self.cloudy = Cloudyday()
        self.state = self.sunny

    def change_state(self, weather):
        if weather == "sunny":
            self.state = self.sunny
        elif weather == "rain":
            self.state = self.rainy
        elif weather == "cloud":
            self.state = self.cloudy
        else:
            raise ValueError("change_state method must be in {}".format(["sunny", "rain", "cloud"]))

    def umbrella(self):
        self.state.umbrella()

    def laundry(self):
        self.state.laundry()


if __name__ == "__main__":
    main()

# 出力

bring no umbrella dry outdoor --- bring normal umbrella dry indoor

特徴

関数とif文でも同じことはできます。

def umbrella(weather):
    if weather == "sunny":
        print("bring no umbrella")
    elif weather == "rain":
        print("bring folding umbrella")
    elif weather == "cloud":
        print("bring normal umbrella")


def laundry(weather):
    # こちらもif文を用いて書き換える必要がある

関数が多くない場合、if文を書き換える手間はあまり多くありませんが、
関数が多い場合、全ての関数を見てif文を書き換える手間が増えます。

Stateパターンを使用した実装では、新しいState追加時に

  • 新しいStateに対応したクラス
  • Context内のif文

を書き換えるだけで対応可能です。

変更点をより少ない場所に押し込めるということがこのパターンで達成することができます。

Pythonらしく書くためのコツ

特になし

クラス図

# 一般的なクラス図

一般的なクラス図(Stateパターン)

# サンプルコードにおけるクラス図

サンプルコードにおけるクラス図(Stateパターン)

参考URL