ソフトウェアデザインパターンは、特定の問題を解決するためのコードの再利用可能な青写真を提供することで、開発プロセスの加速を支援します。
私たちは、デザインパターンに従って、一般化され、再利用可能で、読みやすいコードを記述します。
ということです。
デザインパターンは、同じ問題を解決するソフトウェアエンジニアの累積的な経験をカプセル化し、設計に関連する共通の問題に対する解決策を表します。
デザインパターンは、どのような問題を解決するかによって分類されており、Observer Design PatternはBehavioral Patternに属します。
このクラスは、オブジェクトが互いにどのように通信するかを決定するパターンです。
このガイドでは、Observerデザインパターンについて知る必要のあるすべてのことを学び、ある問題を効率的に解決するためにどのようにそれを使うことができるかを理解することができます。
オブザーバーデザインパターン
Observer Design Pattern は One-to-Many の関係を扱い、購読しているエンティティに observable の変更について知らせるためにイベントを利用します。
これらのイベントのソースは、ストリームとしてイベントを送信するsubjectまたはobservableと呼ばれます。
オブザーバーやシンクはイベントを得るためにオブザーバブルにサブスクライブすることができます。
observableはオブザーバのリストを追跡し、observableの状態が変化したときにその変化を通知します。
この機能には多くの意味合いと実装があり、似たような機能はあなたの周りにもたくさんあります。
非常にシンプルでありながら、非常に効果的で広く普及しているパターンです。
このデザインパターンの同様の実装は、ソーシャルプラットフォームのフィードを生成する際に見られます – Pub/Sub (Publisher/Subscriber) Model/Pattern です。
コンテンツ・パブリッシャーが投稿を公開すると、購読者はその内容を通知されます。
例えるなら、あるイベントの発炎筒や花火に注目する人がいて、それぞれの役割に応じて反応する(しない)ようなものです。
Observer Design PatternとPublish/Subscribe Patternは同じということなのでしょうか?
以前は、両パターンは同義でした。
現在では、それぞれのパターンに明確な特徴があるため、2つの別パターンとなっています。
ObserverパターンとPub/Subパターンの大きな違いは以下のとおりです。
- ObserversとSubjectsは密に結合している。SubjectはObserverを追跡する必要がある。一方、Pub/Subパターンでは、ObserverとSubjectの間にメッセージキューがあり、疎結合である。
- SubjectsからObserversへ同期的にイベントが渡される。しかし、Pub/Subパターンでは、イベントは非同期で受け渡される。
- Observerパターンでは、SubjectsとObserversの両方が同じアプリケーションのロケールに存在するのに対し、Pub/Subパターンでは異なるロケールに存在することができる。
このパターンを理解するのに最適な方法の一つは、それを実装することです。
実装
基本的な実装では、Observable
と Observer
の 2 つのクラスが必要である。
Observerクラスはオブジェクトを引数として初期化される。
このオブジェクトはObservable` であり、生成時にサブスクライブしたオブジェクトを記録しておく。
このクラスには notify()
関数もあり、observable からの通知やイベントの受信を確認するためのトリガーとなります。
class Observer:
def __init__(self, observable):
observable.subscribe(self)
def notify(
self,
observable,
*args,
**kwargs
):
print ('Got', args, kwargs, 'From', observable)
Observableクラスは
Observerインスタンスを保持するために空のリストで初期化されます。
また、オブザーバを追加するためのsubscribe()、各オブザーバの
notify()関数を呼び出すための
notify_observers()、リストからオブザーバを削除するための
unsubscribe()` などの関数も持っています。
class Observable:
def __init__(self):
self._observers = []
def subscribe(self, observer):
self._observers.append(observer)
def notify_observers(self, *args, **kwargs):
for obs in self._observers:
obs.notify(self, *args, **kwargs)
def unsubscribe(self, observer):
self._observers.remove(observer)
上記のコンポーネントをすべて組み込み、オブザーバーと observable をセットアップしてメッセージを送信し、リアクションを引き起こすコードを書いてみましょう。
# observer_pattern.py
"""
Demonstrating the Observer pattern implementation
"""
# Initializing the subject
subject = Observable()
# Initializing twp observers with the subject object
observer1 = Observer(subject)
observer2 = Observer(subject)
# The following message will be notified to 2 observers
subject.notify_observers('This is the 1st broadcast',
kw='From the Observer')
subject.unsubscribe(observer2)
# The following message will be notified to just 1 observer since
# the observer has been unsubscribed
subject.notify_observers('This is the 2nd broadcast',
kw='From the Observer')
2 番目のメッセージを発行する前に observer の登録を解除していることに注意してください。
これにより、2回目の試行では、メッセージは2回ではなく、1回だけ表示されるようになり、1人のサブスクライバのみがメッセージを受信することになります。
このコードを実行すると、次のような結果になります。
$ python observer_pattern.py
Got ('This is the 1st broadcast',) {'kw': 'From the Observer'} From <__main__.Observable object at 0x7f6c50d2fb50>
Got ('This is the 1st broadcast',) {'kw': 'From the Observer'} From <__main__.Observable object at 0x7f6c50d2fb50>
Got ('This is the 2nd broadcast',) {'kw': 'From the Observer'} From <__main__.Observable object at 0x7f6c50d2fb50>
ご覧のように、observable は observers と直接相互作用することができ、その逆もまた可能です。
observer が observable の購読者リストに登録されている限り、observable は observer と相互作用することができます。
長所と短所
実装が整ったところで、このデザインパターンの長所と短所を比較すると、以下のようになります。
長所
- オブジェクトの間に一対多の関係が定義されている。これは、あるオブジェクトが変更されると、依存するオブジェクトに適用される変更のカスケードが発生することを保証するものです。
- 疎結合のオブジェクトは、コンポーネントを交換することができることを意味します。
短所
- observableとobserverの間の通信は同期で、イベントの購読と購読解除の負荷が増加すると、observableオブジェクトは要求であふれかえるかもしれません。これは、各リクエストのスリープ時間を設定することで軽減されます。
- スリープソリューションはまた、速度、パフォーマンス、イベントの損失の可能性を引き起こす可能性があります。これが、Pub/Subパターンがパブリッシャーとサブスクライバーの間にメッセージキューを持つ主な理由でした。
- このパターンでは、オブザーバとオブザーバブルの間に強い参照があるため、メモリリークがよく起こります。observableはobservableオブジェクトから強制的に登録解除される必要があります。
これらの問題を解決するために、Observer と observable の間にメッセージキューが導入され、Observer パターンのバリエーションである Pub/Sub パターンが考案されました。
結論
このガイドでは、Observerパターンについて、その実装方法、長所と短所を比較しました。
Observerパターンは、RSSフィードやソーシャルメディアフィードなど、今日私たちが使っている多くの機能を生み出したBehavioral Patternsの1つであることは、興味深いことです。
Design Patternsのニュアンスを知ることで、機能を一から構築することが容易になる。
そしてもちろん、さまざまなデザインパターンを知ることで、さまざまなタイプの問題に対して最適なソリューションを構築することができるようになる。