python Observerパターンの初歩

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

[デザインパターン「Observer」](https://qiita.com/shoheiyokoyama/items/d4b844ed29f84a80795b)
[にゃんこで分かるObserverパターン 前編]
(https://qiita.com/lookman/items/0cfd05702678738ab336)
[Pythonによるデザインパターン【Observer】-本日のニュースをお届けします-]
(https://qiita.com/kotetsu75/items/719a8fc2e4cc7789c1b4)
[GOFデザインパターンをPythonで - Observer]
(http://atsuya046.hatenablog.com/entry/2014/02/11/212452)

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

#はじめに

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

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

![スクリーンショット 2018-11-10 15.07.48.png](https://qiita-image-store.s3.amazonaws.com/0/305953/a5481f4c-d060-4826-2126-ffd89cd54e51.png)





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

ある人間Aとpress/releaseの状態を持つButtonが存在するとします
AはButtonが押された事を感知して、その後なんらかのreactionを起こしたいと考えているとします
![スクリーンショット 2018-11-10 11.44.18.png](https://qiita-image-store.s3.amazonaws.com/0/305953/178de962-b273-5c39-527a-67101206272f.png)

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



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

```python
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](https://qiita-image-store.s3.amazonaws.com/0/305953/65858f8a-841b-12ad-4d33-9e42cb6c932a.png)




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

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

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

##1. 自身の状態の変化の感知
自身の属性値の変化を感知するのにはpropertyのsetterを利用します
(プロパティについて分からない場合、参考記事として [python property](https://qiita.com/Sylba2050/items/d6f23ac13a0cc5da0c17) がとても分かりやすいと思います)

```python

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の登録できるようにします

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

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

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

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


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

```python

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に一つしか登録できないような記述をしてしまっているからです

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

![スクリーンショット 2018-11-10 15.53.18.png](https://qiita-image-store.s3.amazonaws.com/0/305953/afb24a6c-be75-5cb9-8ed5-cf9219b38acd.png)

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

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

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

![スクリーンショット 2018-11-10 13.35.12.png](https://qiita-image-store.s3.amazonaws.com/0/305953/9682c646-3dd5-012a-cfdd-51ab46dab526.png)


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

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

```python
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メソッドを実行させるようにします

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

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

```python

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をする仕組みを実現できました