pythonによるデザインパターン[Observer]

# Intro

この文章は結城 浩さんの「Java言語で学ぶデザインパターン入門」を、pythonで実装してみたサンプルコードです。

筆者の環境は以下の通りです。Python 3.6.3

まだ修行中の身なので間違いがあると思いますがご了承ください。 今回は「Observer」です。

# Summarise

本書のp256によると、 Observerとは

観察対象の状態が変化すると、観察者に対して通知されます。Observerパターンは、状態変化に応じた処理を記述するときに有効

とあります。Observerは、処理と観察者の結合を委譲(delegation)によってゆるめ、具体的な実装を気にせず状態変化に応じた処理を記述できるようになります。

# Practice

## Specification

乱数を20回生成し、それを2つの方法で観察します。

1つ目の方法は観察した数を数字で表示し、 class DigitObserver で実装します。例えば数字30を観察するときは以下のように表示します。

DigitObservser: 30

2つ目の方法は観察した数を「*」の個数で表示し、 class GraphObserver で実装します。例えば数字30を観察するときは以下のように表示します。

******************************

## Explanation

最初に乱数を発生する処理を記述しましょう。

今回は乱数を発生させるという処理だけなので、具体的なクラス一つを記述するだけでも良いですが、今後処理が増えていくことを予想して抽象クラスを作りましょう。

数を生成する抽象クラスclass NumberGanerator を作成します。数字を取得する def get_nuber 、処理を実行する def execute の実装はサブクラスに任せておいて、重要なのはList型の self.__oberserver です。

ここで委譲(delegation)によって処理が観察者(Observer)と結びついています。 def add_oberser ,def delete_observer で観察者の追加・削除を行い、 def notify_observer で観察者(Observer)の具体的な処理を行います。 def update で実際に観察者が通知を行いますが、後から出てくるように観察者(Observer)も抽象クラスにまとめているので、具体的な実装を気にせず処理を記述できます。

次に処理の具体的な実装を行います。

先ほどの抽象クラスclass NumberGanerator を継承し、乱数を出力するようなプログラムを組みます。数字を取得する def get_nuber 、処理を実行する def execute の実装を具体的なものに落とし込みます。

次に観察者の抽象クラス class Observer を作成します。

下で見るようにメソッドは def update しかありません。 def updatは処理が行われたのを観察者が通知するためのメソッドです。重要なのは観察者を抽象クラスでまとめ上げる点です。観察者と処理はそれぞれ抽象クラスの委譲(delegation)で結びついているので、具体的な実装を気にせず処理を記述できます(同じことを何度も言いますが)。

例えば、以下でもdef update の引数に ganerator があります。pythonでは引数の型を明示しなくても動くので気にならないかもしれませんが、これは処理の抽象クラスclass NumberGanerator のインスタンスをとります。処理を抽象クラスにまとめることで、観察者は処理の具体的な実装を気にしなくても良くなります。

class Observer(metaclass=ABCMeta):
'''観察者を表す抽象クラス'''
@abstractmethod
def update(self, ganerator):
pass

最後は観察者の具体的なクラスを実装していくだけです。

観察した数を数字で表示する class DigitObserver と、観察した数を「*」の個数で表示するclass GraphObserver で実装しますが、細かい実装はどうでも良いので説明は省きます。

main関数では以下の順に実行すれば終わりです。

  1. 処理を作る
  2. 観察者を作る
  3. 処理に観察者を加える def add_observer
  4. 処理を実行 def execute
generator = RandomNumberGenerator()
observer1 = DigitObserver()
generator.add_observer(observer1)
generator.execute()

## Sample Code

python3 observer.py  DigitObservser: 9
*********
DigitObservser: 10
**********
DigitObservser: 35
***********************************
DigitObservser: 23
***********************
....(以下略)

# Conclusion

このパターンは抽象クラスどうしを委譲(delegation)で結びつけているだけなので、それほど難しくないですね。これまでこのような抽象クラスと委譲のテクニックはよく出てくるので、一度下にまとめましょう(p263)。

  • 抽象クラスやインターフェースを使って、具象クラスから抽象メソッドをひきはがす
  • 引数でインスタンスを渡す時や、フィールドでインスタンスを保持するときには、具象クラスの型にしないで、抽象クラスやインターフェースの型にしておく

またObserverはMVCの概念にも生かされているそうです(p266)。ModelとViewerの関係が観察者(Observer)と処理の関係と対応しているらしいです。Viwerが処理で、観察者(Observer)がModelなのかな…🙃🙃🙃わかったようで、わからないような気分です。

抽象クラスと委譲(delegation)ということで、これまでに何度も出た構成ですが、やっと慣れてきました。抽象クラスと委譲(delegation)を意識したコーディングができるようになりたいですね。

# ref

--

--