#参考にさせていただいた記事
デザインパターン「Observer」
[にゃんこで分かる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へ通知する例
#単純な例
まず、Observerが一つの単純な例を考えます
ある人間Aとpress/releaseの状態を持つButtonが存在するとします
AはButtonが押された事を感知して、その後なんらかのreactionを起こしたいと考えているとします
この状態で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()
単純な例を実現する為に以下の2つの機能を追加します
- Subjectが自身の状態の変化を感知する機能
- 状態変化時に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の登録
この状態ではより汎用的なObserverパターンとして利用できないのでこれを解決することを考えます
この問題は以下を導入する事で簡単に解決できます
- Subjectに通知すべきObserverリスト(通知者一覧)をもたせる
- 状態変化通知時(notisfyメソッド実行時)にリストに登録されたObserver全てにreactionメソッドを実行させる
##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をする仕組みを実現できました