LoginSignup
23
20

More than 3 years have passed since last update.

python Observerパターンの初歩

Last updated at Posted at 2018-11-10

参考にさせていただいた記事

デザインパターン「Observer」
にゃんこで分かるObserverパターン 前編
Pythonによるデザインパターン【Observer】-本日のニュースをお届けします-
GOFデザインパターンをPythonで - Observer

とても分かりやすく理解の参考にさせていただきました

はじめに

Observerパターンとは
"監視対象(Subject)が変化した時に監視者(Observer)に通知する仕組み"
のことです
今回以下のような具体例を考え,pythonでの構築の道筋を辿っていきたいと思います

↓Buttonの状態はpress/releaseがあり、それが変化した時にObserverへ通知する例

スクリーンショット 2018-11-10 15.07.48.png

単純な例

まず、Observerが一つの単純な例を考えます

ある人間Aとpress/releaseの状態を持つButtonが存在するとします
AはButtonが押された事を感知して、その後なんらかのreactionを起こしたいと考えているとします
スクリーンショット 2018-11-10 11.44.18.png

この状態でAはButtonの状態を監視して変化を感知してReactionしていると見る事ができ、
この時Aが監視者(Observer), Buttonが監視対象(Subject)とするObserverパターンと考えることが出来ます

実現への道筋

上の単純な具体例をpythonによって実際に実現したいと思います
まず、Subjectが状態を持ち、Observerがreactionメソッドを持つという状況を示すと以下のようになります

class Subject:
    def __init__(self):
        self.button_state = 0

class Observer:
    def reaction(self):
        pass

if __name__ == "__main__":
    Button = Subject()
    A = Observer()

スクリーンショット 2018-11-10 13.55.30.png

単純な例を実現する為に以下の2つの機能を追加します

  1. Subjectが自身の状態の変化を感知する機能
  2. 状態変化時にObserverにreactionメソッドを実行させる機能

それぞれ実現方法を見ていきます

1. 自身の状態の変化の感知

自身の属性値の変化を感知するのにはpropertyのsetterを利用します
(プロパティについて分からない場合、参考記事として python property がとても分かりやすいと思います)


class Subject:
    def __init__(self):
        self._button_state = 0

    @property
    def button_state(self):
        return self._button_state

    @button_state.setter
    def button_state(self,state):
        if self._button_state != state:
            self._button_state = state
            print("buttonの状態が変化")

class Observer:
    def reaction(self):
        pass


if __name__ == "__main__":
    A = Observer()
    Button = Subject()

    Button.button_state = 1    #=>buttonの状態が変化

2.状態変化時にObserverに通知する機能

まず、通知すべきObserverの登録できるようにします

class Subject:
    def __init__(self,observer):
        self._button_state = 0
        self.observer = observer #追加

次にObserverのreactionを発生させるnotisfyメソッドを追加します
(ここがObserverへの通知することと同様の意味を持つ)
また、notisfyメソッドが状態変化時に発生するようにsetter内からこれを呼び出します

class Subject:
    #----省略----
    @button_state.setter
    def button_state(self,state):
        self._button_state = state
        self.notisfy() #追加

    def notisfy(self): #追加
        self.observer.reaction()

全体

よって全体は以下のようになります


class Subject:
    def __init__(self,observer): #追加
        self._button_state = 0
        self.observer = observer #追加

    @property
    def button_state(self):
        return self._button_state

    @button_state.setter
    def button_state(self,state):
        self._button_state = state
        self.notisfy() #追加

    def notisfy(self): #追加
        self.observer.reaction()


class Observer:
    def reaction(self):
        print("reaction") #変更


if __name__ == "__main__":
    A = Observer()
    Button = Subject(A) #変更

    Button.button_state = 1    #=>reaction
    Button.button_state = 0    #=>reaction

以上でSubjectオブジェクトのbutton_stateが変化したときにそれを監視している一つのObserverがreactionを起こすという単純な例が実現できました

より汎用的なObserverパターン

単純な例を実現することができましたが、このままでは1つのSubjectに対して複数のObserverに通知したい時に対応できません
なぜなら、通知すべきObserverに一つしか登録できないような記述をしてしまっているからです

class Subject:
    def __init__(self,observer):
        self._button_state = 0
        self.observer = observer #通知すべきObserverの登録

スクリーンショット 2018-11-10 15.53.18.png

この状態ではより汎用的なObserverパターンとして利用できないのでこれを解決することを考えます

この問題は以下を導入する事で簡単に解決できます

  1. Subjectに通知すべきObserverリスト(通知者一覧)をもたせる
  2. 状態変化通知時(notisfyメソッド実行時)にリストに登録されたObserver全てにreactionメソッドを実行させる

スクリーンショット 2018-11-10 13.35.12.png

1. SubjectにObserverリスト(通知者一覧)をもたせる

Observerリストとそのリストにobserverオブジェクトを追加するメソッドを書き加えます

class Subject:
    def __init__(self): #変更
        self._button_state = 0
        self.observers = [] #変更

    def add_observer(self,observer): #追加
        self.observers.append(observer)

2. 状態変化時にリスト内全てのObserverにreactionメソッドを実行させる

notisfyメソッドの処理を書き換えてリスト内全てのobserverにreactionメソッドを実行させるようにします

class Subject:
    #----省略----
    def notisfy(self): #変更
        for i in self.observers:
            i.reaction()

全体

以上より全体は以下のようになります


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

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

    @property
    def button_state(self):
        return self._button_state

    @button_state.setter
    def button_state(self,state):
        if self._button_state != state:
            self._button_state = state
            self.notisfy()

    def notisfy(self):
        for i in self.observers:
            i.reaction()


class Observer:
    def reaction(self):
        print("ボタンが押された")


if __name__ == "__main__":
    A = Observer()
    B = Observer()
    C = Observer()

    Button = Subject()

    Button.add_observer(A) #observerリストに追加
    Button.add_observer(B) #observerリストに追加
    Button.add_observer(C) #observerリストに追加

    Button.button_state = 0
    Button.button_state = 1
    Button.button_state = 0
    Button.button_state = 0

以上でSubjectオブジェクトに登録された複数のObserver全てが監視している属性値が変化したときにreactionをする仕組みを実現できました

23
20
1

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
23
20