Pythonで、デザインパターン「Observer」を学ぶ

GoFのデザインパターンを学習する素材として、書籍「[増補改訂版Java言語で学ぶデザインパターン入門](https://www.hyuki.com/dp/)」が参考になるみたいですね。ただ、取り上げられている実例は、JAVAベースのため、自分の理解を深めるためにも、Pythonで同等のプラクティスに挑んでみました。

# ■ Observerパターン（オブザーバ・パターン）
Observerパターンとは、プログラム内のオブジェクトのイベント( 事象 )を他のオブジェクトへ通知する処理で使われるデザインパターンの一種。
通知するオブジェクト側が、通知されるオブジェクト側に観察（英: observe）される形になる事から、こう呼ばれる。
出版-購読型モデルとも呼ばれる。暗黙的呼び出しの原則と関係が深い。
分散イベント処理システムの実装にも使われる。言語によっては、このパターンで扱われる問題は言語が持つイベント処理構文で処理される。

## UML class and sequence diagram
![W3sDesign_Observer_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/363e09a2-e0cb-5463-cff3-246229bbfaa8.jpeg)
## UML class diagram
![2880px-Observer_w_update.svg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/86f40e9c-0509-be6e-f93e-0f51f9a061d2.png)

[(以上、ウィキペディア（Wikipedia）より引用)](https://en.wikipedia.org/wiki/Observer_pattern)

## □ 備忘録
`Observer`パターンでは、観察対象の状態が変化すると、観察者に対して通知されるので、状態変化に応じた処理を記述するときに有効だそうです。
`observer`という言葉の本来の意味は「観察者」ですが、実際には、`Observer`役は能動的に「観察」するのではなく、`Subject`役から「通知」されるのを受動的に待っていることになるので、`Publish-Subscribe`パターンと呼ばれることもあるそうです。
確かに、`publish（発行）`と`subscribe（購読）`という表現の方が、適切のような気がしました。

# ■ "Observer"のサンプルプログラム
実際に、Observerパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。

- 数をたくさん生成するオブジェクトを観察者が観察して、その値を表示する仕組みになります
- 表示の方法は、観察者によって異なります。
- `DigitalObserver`は、値を数字で表示します
- `GraphicObserver`は、値を簡易グラフで表示します



```console
$ python Main.py 
DigitObservser: 30
GraphicObserver:******************************
DigitObservser: 48
GraphicObserver:************************************************
DigitObservser: 6
GraphicObserver:******
DigitObservser: 19
GraphicObserver:*******************
DigitObservser: 19
GraphicObserver:*******************
DigitObservser: 45
GraphicObserver:*********************************************
DigitObservser: 8
GraphicObserver:********
DigitObservser: 21
GraphicObserver:*********************
DigitObservser: 40
GraphicObserver:****************************************
DigitObservser: 6
GraphicObserver:******
DigitObservser: 1
GraphicObserver:*
DigitObservser: 9
GraphicObserver:*********
DigitObservser: 26
GraphicObserver:**************************
DigitObservser: 22
GraphicObserver:**********************
DigitObservser: 16
GraphicObserver:****************
DigitObservser: 10
GraphicObserver:**********
DigitObservser: 45
GraphicObserver:*********************************************
DigitObservser: 1
GraphicObserver:*
DigitObservser: 36
GraphicObserver:************************************
DigitObservser: 45
GraphicObserver:*********************************************
```

# ■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern/tree/master/Observer

- ディレクトリ構成

```console
.
├── Main.py
└── observer
    ├── __init__.py
    ├── generator.py
    └── observer.py
```

## (1) Subject（被験者）の役
`Subject`役は、「観察される側」を表します。`Subject`役は、観察者である`Observer`役を登録するメソッドと、削除するメソッドを持っています。また、「現在の状態を取得する」メソッドも宣言されています。
サンプルプログラムでは、`NumberGenerator`クラスが、この役を努めます。

```python:observer/generator.py
import random
from abc import ABCMeta, abstractmethod

class NumberGenerator(metaclass=ABCMeta):
    def __init__(self):
        self.__observers = []

    def addObserver(self, observer):
        self.__observers.append(observer)

    def deleteObserver(self, observer):
        self.__observers.remove(observer)

    def notifyObserver(self):
        for o in self.__observers:
            o.update(self)

    @abstractmethod
    def getNumber(self):
        pass

    @abstractmethod
    def execute(self):
        pass
```
## (2) ConcreteSubject（具体的な被験者）の役
`ConcreteSubject`役は、具体的な「観察される側」を表現する役です。状態が変化したら、そのことを登録されている`Observer`役に伝えます。
サンプルプログラムでは、`RandomNumberGenerator`クラスが、この役を努めます。

```python:observer/generator.py
class RandomNumberGenerator(NumberGenerator):
    def __init__(self):
        self.__number = 0
        super(RandomNumberGenerator, self).__init__()

    def getNumber(self):
        return self.__number

    def execute(self):
        for _ in range(20):
            self.__number = random.randint(0, 49)
            self.notifyObserver()
```
## (3) Observer（観察者）の役
`Observer`役は、`Subject`役から「状態が変化しましたよ」と教えてもらう役です。そのためのメソッドが`update`です。
サンプルプログラムでは、`Observer`クラスが、この役を努めます。

```python:observer/observer.py
import time
from abc import ABCMeta, abstractmethod

class Observer(metaclass=ABCMeta):
    @abstractmethod
    def update(self, generator):
        pass
```
## (4) ConcreteObserver（具体的な観察者）の役
`ConcreteObserver`役は、具体的な`Observer`です。`update`メソッドが呼び出されると、そのメソッドの中で`Subject`役の現在の状態を取得します。
サンプルプログラムでは、`DigitObserver`クラスと`GraphObserver`クラスが、この役を努めます。

```python:observer/observer.py
class DigitObserver(Observer):
    def update(self, generator):
        print("DigitObservser: {0}".format(generator.getNumber()))
        time.sleep(0.1)


class GraphObserver(Observer):
    def update(self, generator):
        print("GraphicObserver:", end='')
        count = generator.getNumber()
        for _ in range(count):
            print('*', end='')
        print("")
        time.sleep(0.1)
```
## (5) Client（依頼人）の役
サンプルプログラムでは、`startMain`メソッドが、この役を努めます。

```python:Main.py
from observer.observer import  DigitObserver, GraphObserver
from observer.generator import RandomNumberGenerator

def startMain():
    generator = RandomNumberGenerator()
    observer1 = DigitObserver()
    observer2 = GraphObserver()
    generator.addObserver(observer1)
    generator.addObserver(observer2)
    generator.execute()

if __name__ == '__main__':
    startMain()
```

# ■ 参考URL
- [「Java言語で学ぶデザインパターン入門」を終えて(ない)](https://medium.com/since-i-want-to-start-blog-that-looks-like-men-do/java言語で学ぶデザインパターン入門-を終えて-ない-2cc9b34a30b2)