Posted at

デザインパターン(Design Pattern)#Observer

More than 3 years have passed since last update.

設計を意識したコードが書けるようになる為に、デザインパターン修行しました。

他のDesign Patternもちょくちょく出していきます。


前置き

デザインパターンをどういう時に、何を、どう使うのかを理解することが一先ずの目標。

(Javaというか静的型付言語は初めてで、且つpython歴もそんなに長くないので、Pythonistaぽっくないところがあると思います。ご指摘ございましたらご教授ください。)

今回は、振る舞いに関するパターンObserver。


Observerとは

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


概要

ここで作るサンプルプログラムは、数をたくさん生成するオブジェクトを観察者が観察して、その値を表示するというものです。ただし、表示の方法は観察者によって異なります。DigitObserverは値を数字で表示しますが、GraphObserverは値を簡易グラフで表示します。


全体のクラス図


observer.py

from abc import ABCMeta, abstractmethod

class Observer(Exception):
__meta__ = ABCMeta

@abstractmethod
def update(self, generator):
pass


observerインタフェースは、「観察者」を表現するインタフェースです。具体的な観察者は、このインタフェースで実装します。

updateメソッドを呼び出すのは、数を生成するNumberGeneratorです。

updateメソッドはNumberGeneratorが「私の内容が更新されました。表示の方も更新してください」とobserverに伝えるためのメソッドです。



number_generator.py

from abc import ABCMeta, abstractmethod

class NumberGenerator(metaclass=ABCMeta):

__observers = []

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

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

def notify_observers(self):
for observer in self.__observers:
observer.update(self)

@abstractmethod
def get_number():
pass

@abstractmethod
def execute():
pass


NumberGeneratorクラスは数を生成する抽象クラスです。実際の数の生成(executeメソッド)と、数を取得する部分(get_numberメソッド)はサブクラスが実装することを期待して、抽象メソッドになっています。

observersフィールドは、NumberGeneratorを観察しているobserverたちを保存しているフィールドです。

add_observerはobserverを追加するメソッド、delete_observerはobserverを削除するメソッドです。

notify_observersメソッドは、observer全員に対して「私の内容が更新されたので、あなたの表示を更新してください」と伝えるものです。このメソッドの中では、observersの中のobserverたち1人1人のupdateメソッドを呼び出しています。



random_number_generator.py

import random

from number_generator import NumberGenerator

class RandomNumberGenerator(NumberGenerator):

__number = 0

def __init__(self):
self.__rand = random

def get_number(self):
return self.__number

def execute(self):
for i in range(0, 20):
self.__number = self.__rand.randint(0, 50)
self.notify_observers()


RandomNumberGeneratorクラスは、NumberGeneratorのサブクラスで乱数を発生するものです。

randomフィールドには、乱数発生器が保持され、numberフィールドには現在の乱数値が保持されます。

get_numberメソッドは、numberフィールドの値を返します。

executeメソッドは乱数(0~49の整数)を20個生成し、そのつどnotify_observersを使って、観察者に通知をします。



digit_observer.py

import time

import logging
from observer import Observer

class DigitObserver(Observer):

def update(self, generator):
print('DigitObserver:' + str(generator.get_number()))
try:
time.sleep(1)
except InterruptedError as e:
logging.exception(e)


DigitObserverクラスは、observerインタフェースを実装しているクラスで、観察した数を「数字」で表示するためのものです。updateメソッドの中で引数として与えられたNumberGeneratorのget_numberメソッドを使って数を取得し、表示します。表示の様子がわかるように間隔をあけています。



graph_observer.py

import sys

import time
from observer import Observer

class GraphObserver(Observer):

def update(self, generator):
sys.stdout.write('GraphObserver:')
count = generator.get_number()
for i in range(0, count):
sys.stdout.write('*')
print('')
try:
time.sleep(1)
except InterruptedError:
pass


GraphObserverクラスも、Observerインタフェースを実装しているクラスです。このクラスは観察した数を*****のような「簡易グラフ」で表します。



main.py

from digit_observer import DigitObserver

from graph_observer import GraphObserver
from random_number_generator import RandomNumberGenerator

def main():
generator = RandomNumberGenerator()
observer1 = DigitObserver()
observer2 = GraphObserver()
generator.add_observer(observer1)
generator.add_observer(observer2)
generator.execute()

if __name__ == "__main__":
main()


RandomNumberGeneratorのインスタンスを1個作り、その観察者を2個作ります。observer1はDigitObserverの、observer2はGraphObserverのインスタンスです。

add_observerメソッドを使って観察者を登録した後、generator.executeを使って数を生成します。

実行結果

DigitObserver:17

GraphObserver:*****************
DigitObserver:43
GraphObserver:*******************************************
DigitObserver:47
GraphObserver:***********************************************
DigitObserver:34
GraphObserver:**********************************
DigitObserver:30
GraphObserver:******************************
DigitObserver:50
GraphObserver:**************************************************
DigitObserver:7
GraphObserver:*******
DigitObserver:40
GraphObserver:****************************************
DigitObserver:39
GraphObserver:***************************************
DigitObserver:41
GraphObserver:*****************************************
DigitObserver:38
GraphObserver:**************************************
DigitObserver:3
GraphObserver:***
DigitObserver:22
GraphObserver:**********************
DigitObserver:26
GraphObserver:**************************
DigitObserver:0
GraphObserver:
DigitObserver:23
GraphObserver:***********************


まとめ

一方のオブジェクトがもう一方のオブジェクト(観察者) を登録するメソッドを追加することによって、監視対象となりました。監視対象オブジェクトはその変更時に登録オブザーバーにメッセージを送信します。

オブザーバーがその情報をどう処理するかは、監視対象オブジェクトにとっては関係なく、どんなクラスのオブジェクトでも構いません。

オブジェクトは必ずしも理由を理解することなく、その結果、オブジェクト間の依存度を下げることができ、通知先の管理を観察者が行うことで、観察対象は通知側を意識する必要が無くなりました。


参考