Menu
Coddy logo textTech

Observer パターン

CoddyのPythonジャーニー「Object Oriented Programming」セクションの一部 — レッスン 47/64。

オブザーバーパターンは、あるオブジェクト(サブジェクト)の状態が変化した際に、複数のオブジェクト(オブザーバー)に通知を行う1対多のリレーションシップを作成します。

以下は、オブザーバーを管理するシンプルなSubjectクラスです:

class Subject:
    def __init__(self):
        self._observers = []
    
    def add_observer(self, observer):
        self._observers.append(observer)
    
    def notify(self, message):
        for observer in self._observers:
            observer.update(message)

サブジェクトはオブザーバーのリストを保持し、それらすべてに一度に通知することができます。

シンプルなオブザーバークラスを作成します:

class EmailNotifier:
    def update(self, message):
        print(f"Email sent: {message}")

class SMSNotifier:
    def update(self, message):
        print(f"SMS sent: {message}")

各オブザーバーは、通知されたときに呼び出される update メソッドを持っています。

オブザーバーパターンを使用します:

# Subjectを作成
news = Subject()

# Observerを作成
email = EmailNotifier()
sms = SMSNotifier()

# SubjectにObserverを追加
news.add_observer(email)
news.add_observer(sms)

# すべてのObserverに通知
news.notify("Breaking news: Python is awesome!")

株価トラッカーを使用した実用的な例を作成しましょう:

class Stock:
    def __init__(self, symbol, price):
        self.symbol = symbol
        self._price = price
        self._observers = []
    
    def add_observer(self, observer):
        self._observers.append(observer)
    
    def set_price(self, price):
        self._price = price
        self.notify()
    
    def notify(self):
        for observer in self._observers:
            observer.update(self.symbol, self._price)

class Investor:
    def __init__(self, name):
        self.name = name
    
    def update(self, symbol, price):
        print(f"{self.name} notified: {symbol} is now ${price}")

# 株価トラッカーを使用する
apple_stock = Stock("AAPL", 150)

investor1 = Investor("Alice")
investor2 = Investor("Bob")

apple_stock.add_observer(investor1)
apple_stock.add_observer(investor2)

apple_stock.set_price(155)  # すべての投資家に通知する

出力:

Email sent: Breaking news: Python is awesome!
SMS sent: Breaking news: Python is awesome!
Alice notified: AAPL is now $155
Bob notified: AAPL is now $155

重要なポイント: オブザーバーパターンを使用すると、あるオブジェクトの状態が変化したときに、他の多くのオブジェクトに自動的に通知することができます。サブジェクトはオブザーバーのリストを保持し、必要に応じてそれらのupdateメソッドを呼び出します。これは、通知、イベントシステム、およびアプリケーションの複数の部分を同期させるのに役立ちます。

challenge icon

チャレンジ

中級

気象観測システムを作成して、オブザーバーパターンを実装してください。コード内の指定された場所に2つのクラスを作成する必要があります。

ステップ 1: WeatherStation クラスの作成

コメントで指示されている場所に WeatherStation クラスを記述してください。このクラスは以下の要件を満たす必要があります:

  • Subject クラスを継承する (class WeatherStation(Subject): を使用)
  • 初期化処理:
    • super().__init__() を使用して親クラスのコンストラクタを呼び出す
    • プライベート属性 self._temperature を使用して、初期温度を 0 に設定する
  • <strong>set_temperature(self, temperature)</strong> の実装:
    • プライベートな温度属性を更新する
    • self.notify(self._temperature) を呼び出して、すべてのオブザーバーに通知する
  • <strong>get_temperature(self)</strong> の実装:
    • 現在の温度値を返す

ステップ 2: WeatherDisplay クラスの作成

コメントで指示されている場所に WeatherDisplay クラスを記述してください。このクラスは以下の要件を満たす必要があります:

  • Observer クラスを継承する (class WeatherDisplay(Observer): を使用)
  • 初期化処理:
    • name パラメータを受け取る
    • 名前を self.name として保存する
  • <strong>update(self, temperature)</strong> の実装:
    • 以下の形式で温度更新メッセージを出力する

メッセージ形式:

ディスプレイが温度更新を受け取ったとき、必ず以下の形式で出力してください:

Display [name]: Current temperature is [temperature]C

使用例:

# Create weather station and displays
station = WeatherStation()
phone_display = WeatherDisplay("Phone")
tablet_display = WeatherDisplay("Tablet")

# Attach displays to station
station.attach(phone_display)
station.attach(tablet_display)

# Update temperature - both displays will be notified
station.set_temperature(25.5)

# Output:
# Display Phone: Current temperature is 25.5C
# Display Tablet: Current temperature is 25.5C

重要な注意事項:

  • attach() メソッドと detach() メソッドは、すでに Subject 基底クラスで実装されています。WeatherStation クラス内でこれらを実装したり、呼び出したりする必要はありません。
  • 上記の例にある station.attach(phone_display) は、WeatherStation の実装内ではなく、クラスの利用者によって呼び出されます。
  • WeatherStation は、温度が変化したときに self.notify() を呼び出すだけで済みます。残りの処理は Subject 基底クラスが担当します。
  • WeatherStation で指定された3つのメソッドと、WeatherDisplay で指定された2つのメソッドの実装に集中してください。

チートシート

Observerパターンは、1つのオブジェクト(サブジェクト)の状態が変化したときに、複数のオブジェクト(オブザーバー)に通知する「1対多」のリレーションシップを作成します。

オブザーバーを管理する基本的なSubjectクラス:

class Subject:
    def __init__(self):
        self._observers = []
    
    def add_observer(self, observer):
        self._observers.append(observer)
    
    def notify(self, message):
        for observer in self._observers:
            observer.update(message)

updateメソッドを持つObserverクラス:

class EmailNotifier:
    def update(self, message):
        print(f"Email sent: {message}")

class SMSNotifier:
    def update(self, message):
        print(f"SMS sent: {message}")

Observerパターンの使用例:

# Subjectを作成
news = Subject()

# Observerを作成
email = EmailNotifier()
sms = SMSNotifier()

# SubjectにObserverを追加
news.add_observer(email)
news.add_observer(sms)

# すべてのObserverに通知
news.notify("Breaking news: Python is awesome!")

株価トラッカーを使用した実践的な例:

class Stock:
    def __init__(self, symbol, price):
        self.symbol = symbol
        self._price = price
        self._observers = []
    
    def add_observer(self, observer):
        self._observers.append(observer)
    
    def set_price(self, price):
        self._price = price
        self.notify()
    
    def notify(self):
        for observer in self._observers:
            observer.update(self.symbol, self._price)

class Investor:
    def __init__(self, name):
        self.name = name
    
    def update(self, symbol, price):
        print(f"{self.name} notified: {symbol} is now ${price}")

Observerパターンを使用すると、何かが変化したときに1つのオブジェクトが他の多くのオブジェクトに自動的に通知できるようになります。サブジェクトはオブザーバーのリストを保持し、必要に応じてそれらのupdateメソッドを呼び出します。

自分で試してみよう

class Subject:
    def __init__(self):
        self._observers = []
    
    def attach(self, observer):
        self._observers.append(observer)
        
    def detach(self, observer):
        self._observers.remove(observer)
        
    def notify(self, data):
        for observer in self._observers:
            observer.update(data)
            
class Observer:
    def update(self, data):
        pass

# ここに WeatherStation クラスを記述してください

# ここに WeatherDisplay クラスを記述してください

quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Object Oriented Programmingのすべてのレッスン