觀察者模式是一種行為型設計模式,它定義了物件間一對多的依賴關係,當一個物件狀態改變時,其所有依賴物件都會收到通知並自動更新。在 Python 中,觀察者模式常被用於事件處理、GUI 程式設計以及資料同步等場景。藉由觀察者模式,我們可以降低程式碼耦合度,提升系統的彈性與可擴充套件性。觀察者模式的核心概念在於將資料變更的通知行為與實際處理邏輯分離,讓程式碼更易於維護和擴充。

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):
        for observer in self.observers:
            observer.update(self)

class Data(Subject):
    def __init__(self, name=''):
        super().__init__()
        self.name = name
        self._data = 0

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, value):
        self._data = value
        self.notify()

class HexFormatterObserver:
    def update(self, subject):
        print(f'HexFormatterObserver: {subject.name} has data 0x{subject.data:x}')

class BinaryFormatterObserver:
    def update(self, subject):
        print(f'BinaryFormatterObserver: {subject.name} has data {subject.data:b}')

def main():
    data1 = Data('Data 1')

    hex_formatter = HexFormatterObserver()
    binary_formatter = BinaryFormatterObserver()

    data1.attach(hex_formatter)
    data1.attach(binary_formatter)

    data1.data = 10


if __name__ == '__main__':
    main()

觀察者模式的優點

  • 解耦:觀察者模式可以將物件之間的耦合減少,提高系統的可維護性和可擴充套件性。
  • 增加彈性:觀察者模式允許物件之間的鬆散耦合,增加了系統的彈性和可擴充套件性。
  • 通知機制:觀察者模式提供了一種通知機制,當物件的狀態改變時,可以通知所有相關的物件。

觀察者模式的實作

觀察者模式的實作通常包括以下幾個步驟:

  1. 定義觀察者介面:定義一個觀察者介面,該介面包含了一個或多個方法,用於接收通知。
  2. 定義被觀察者介面:定義一個被觀察者介面,該介面包含了一個或多個方法,用於新增、刪除和通知觀察者。
  3. 實作觀察者:實作觀察者介面,該實作包含了一個或多個方法,用於接收通知。
  4. 實作被觀察者:實作被觀察者介面,該實作包含了一個或多個方法,用於新增、刪除和通知觀察者。

觀察者模式的應用

觀察者模式的應用包括以下幾個方面:

  • MVC 模式:MVC 模式是一種常見的觀察者模式的應用,模型(Model)是被觀察者,檢視(View)是觀察者。
  • 事件驅動程式設計:事件驅動程式設計是一種常見的觀察者模式的應用,事件源是被觀察者,事件處理器是觀察者。
  • 訊息佇列:訊息佇列是一種常見的觀察者模式的應用,訊息傳送者是被觀察者,訊息接收者是觀察者。

Python 中的觀察者模式

Python 中的觀察者模式可以使用以下幾種方法實作:

  • 使用 Observer 類別:定義一個 Observer 類別,該類別包含了一個或多個方法,用於接收通知。
  • 使用 Subject 類別:定義一個 Subject 類別,該類別包含了一個或多個方法,用於新增、刪除和通知觀察者。
  • 使用 pubsub 模組:Python 的 pubsub 模組提供了一種簡單的觀察者模式的實作。

以下是一個簡單的例子:

class Observer:
    def update(self, message):
        print(f"Received message: {message}")

class Subject:
    def __init__(self):
        self.observers = []

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

    def remove_observer(self, observer):
        self.observers.remove(observer)

    def notify_observers(self, message):
        for observer in self.observers:
            observer.update(message)

# 建立一個主題
subject = Subject()

# 建立一個觀察者
observer = Observer()

# 將觀察者新增到主題中
subject.add_observer(observer)

# 通知所有觀察者
subject.notify_observers("Hello, world!")

這個例子中,Observer 類別定義了一個 update 方法,用於接收通知。Subject 類別定義了一個 add_observer 方法,用於新增觀察者,一個 remove_observer 方法,用於刪除觀察者,一個 notify_observers 方法,用於通知所有觀察者。

觀察者模式的應用與實作

觀察者模式是一種常見的設計模式,當我們想要通知或更新一個或多個物件(觀察者/訂閱者)關於另一個物件(主題/釋出者/可觀察物件)發生的變化時,觀察者模式就派上用場了。觀察者的數量和身份可以動態變化。

觀察者模式的使用案例

觀察者模式在許多情況下都非常有用。例如,RSS、Atom 或其他相關格式的新聞推播,當你關注一個新聞源時,每當新聞源更新時,你就會收到通知。社交網路也是觀察者模式的一個典型應用,當你與某人連線時,當對方更新了內容,你就會收到通知。

事件驅動系統也是觀察者模式的一個常見應用。這種系統中,有事件監聽器監聽特定的事件,當事件發生時,監聽器就會被觸發。事件扮演著釋出者的角色,監聽器則扮演著觀察者的角色。這種模式的關鍵在於,多個監聽器(觀察者)可以附加到單個事件(釋出者)上。

實作觀察者模式

以下是觀察者模式的一個實作範例,使用 Python 來展示觀察者模式的基本結構和功能。這個範例中,我們定義了一個 Publisher 類別,負責管理觀察者和釋出通知。然後,我們定義了一個 DefaultFormatter 類別,繼承自 Publisher,並增加了特定的格式化功能。

class Publisher:
    def __init__(self):
        self.observers = []

    def add(self, observer):
        if observer not in self.observers:
            self.observers.append(observer)
        else:
            print(f"Failed to add: {observer}")

    def remove(self, observer):
        try:
            self.observers.remove(observer)
        except ValueError:
            print(f"Failed to remove: {observer}")

    def notify(self):
        [o.notify(self) for o in self.observers]

class DefaultFormatter(Publisher):
    def __init__(self, name):
        Publisher.__init__(self)
        self.name = name
        self._data = 0  # 使用名稱混淆來表示不應直接存取

    def update_data(self, new_data):
        self._data = new_data
        self.notify()

    def notify(self):
        print(f"{self.name} 的資料更新為 {self._data}")
        super().notify()

# 範例使用
formatter = DefaultFormatter("MyFormatter")

class Observer:
    def __init__(self, name):
        self.name = name

    def notify(self, publisher):
        print(f"{self.name} 收到 {publisher.name} 的通知")

observer1 = Observer("Observer1")
observer2 = Observer("Observer2")

formatter.add(observer1)
formatter.add(observer2)

formatter.update_data(10)

這個範例展示了觀察者模式的基本結構和功能,包括觀察者管理、通知機制和資料更新。觀察者模式是一種強大的工具,可以用於解決許多實際問題,特別是在事件驅動系統和資料更新通知方面。

物件導向程式設計:屬性和通知機制

在物件導向程式設計中,封裝和抽象是兩個重要的概念。封裝指的是將資料和操作資料的方法包裝在一起,以隱藏實作細節;抽象則是指關注物件的外部行為,而不是其內部實作。Python 的屬性(property)機制提供了一種實作封裝和抽象的方法。

屬性機制

Python 的 @property 裝飾器可以用來定義 getter 方法,允許對物件的屬性進行讀取。這使得我們可以將物件的內部狀態以更控制的方式暴露給外部。

class Publisher:
    def __init__(self, name):
        self.name = name
        self._data = 0

    @property
    def data(self):
        return self._data

在這個例子中,data 屬性是一個 getter 方法,允許讀取 _data 的值。

設定器機制

Python 的 @x.setter 裝飾器可以用來定義 setter 方法,允許對物件的屬性進行寫入。這使得我們可以在設定屬性值時進行額外的操作,例如資料驗證或通知。

class Publisher:
    # ...

    @data.setter
    def data(self, new_value):
        try:
            self._data = int(new_value)
        except ValueError as e:
            print(f'Error: {e}')
        else:
            self.notify()

在這個例子中,data 屬性的 setter 方法嘗試將新值轉換為整數,如果失敗則印出錯誤訊息。如果成功則呼叫 notify 方法。

通知機制

通知機制允許物件在其內部狀態改變時通知其他物件。這可以用來實作觀察者模式(Observer Pattern),使得物件之間可以彼此溝通。

class Publisher:
    # ...

    def notify(self):
        print(f"{self.name} 的資料已更新:{self._data}")

在這個例子中,notify 方法簡單地印出一條訊息,指出物件的資料已更新。

完整範例

class Publisher:
    def __init__(self, name):
        self.name = name
        self._data = 0

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, new_value):
        try:
            self._data = int(new_value)
        except ValueError as e:
            print(f'Error: {e}')
        else:
            self.notify()

    def notify(self):
        print(f"{self.name} 的資料已更新:{self._data}")

# 測試
publisher = Publisher("Test Publisher")
publisher.data = "123"
publisher.data = "abc"

這個範例示範瞭如何使用屬性機制和通知機制來實作封裝和抽象。當 data 屬性被設定時,會嘗試將新值轉換為整數,如果成功則呼叫 notify 方法。

觀察者模式的實作

觀察者模式是一種設計模式,允許物件在其他物件發生變化時得到通知。在這個例子中,我們將實作觀察者模式來觀察資料的變化。

觀察者介面

首先,我們定義一個觀察者介面,所有觀察者都必須實作這個介面。這個介面只有一個方法 notify,它會在資料發生變化時被呼叫。

class Observer:
    def notify(self, publisher):
        pass

觀察者實作

接下來,我們實作兩個觀察者:HexFormatterObsBinaryFormatterObs。這兩個觀察者都會在資料發生變化時被通知,並且會將資料格式化為十六進位制和二進位制。

class HexFormatterObs(Observer):
    def notify(self, publisher):
        value = hex(publisher.data)
        print(f"{type(self).__name__}: '{publisher.name}' has now hex data = {value}")

class BinaryFormatterObs(Observer):
    def notify(self, publisher):
        value = bin(publisher.data)
        print(f"{type(self).__name__}: '{publisher.name}' has now bin data = {value}")

被觀察者

被觀察者是 DefaultFormatter 類別,它會維護一份觀察者列表,並且在資料發生變化時通知所有觀察者。

class DefaultFormatter:
    def __init__(self, name):
        self.name = name
        self.data = 0
        self.observers = []

    def add(self, observer):
        self.observers.append(observer)

    def remove(self, observer):
        self.observers.remove(observer)

    def notify_observers(self):
        for observer in self.observers:
            observer.notify(self)

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, value):
        self._data = value
        self.notify_observers()

測試

最後,我們可以測試觀察者模式的實作。

def main():
    df = DefaultFormatter('test1')
    print(df)

    print()

    hf = HexFormatterObs()
    df.add(hf)

    df.data = 3

    print(df)

if __name__ == '__main__':
    main()

這個例子展示了觀察者模式的基本概念和實作。觀察者模式是一種強大的工具,允許我們在物件之間建立鬆散耦合的關係,並且可以用來解決許多複雜的問題。

觀察者模式的實作

觀察者模式是一種設計模式,允許物件在其他物件的狀態改變時得到通知。以下是觀察者模式的實作:

BinaryFormatterObs 類別

class BinaryFormatterObs:
    def update(self, data):
        print(f"BinaryFormatterObs received update: {bin(data)}")

HexFormatterObs 類別

class HexFormatterObs:
    def update(self, data):
        print(f"HexFormatterObs received update: {hex(data)}")

DataFormatter 類別

class DataFormatter:
    def __init__(self):
        self.observers = []
        self.data = None

    def add(self, observer):
        if observer not in self.observers:
            self.observers.append(observer)

    def remove(self, observer):
        if observer in self.observers:
            self.observers.remove(observer)

    def notify(self):
        for observer in self.observers:
            observer.update(self.data)

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, value):
        self._data = value
        self.notify()

例項化和測試

bf = BinaryFormatterObs()
hf = HexFormatterObs()

df = DataFormatter()
df.add(bf)
df.data = 21
print(df.data)

df.remove(hf)
df.data = 40
print(df.data)

df.remove(hf)
df.add(bf)
df.data = 'hello'
print(df.data)

內容解密:

在上述程式碼中,我們定義了三個類別:BinaryFormatterObsHexFormatterObsDataFormatterBinaryFormatterObsHexFormatterObs 是觀察者類別,它們實作了 update 方法,用於接收通知。DataFormatter 是被觀察者類別,它維護了一個觀察者列表,並提供 addremovenotify 方法,用於管理觀察者和傳送通知。

在例項化和測試部分,我們建立了 BinaryFormatterObsHexFormatterObs 的例項,並將它們新增到 DataFormatter 的觀察者列表中。然後,我們設定 DataFormatterdata 屬性,並觀察觀察者如何接收通知。

圖表翻譯:

  flowchart TD
    A[DataFormatter] --> B[BinaryFormatterObs]
    A --> C[HexFormatterObs]
    B --> D[update]
    C --> D
    D --> E[print]

在這個圖表中,我們展示了 DataFormatter 如何與觀察者進行互動。當 DataFormatterdata 屬性改變時,它會通知所有觀察者,然後觀察者會呼叫其 update 方法,並將通知傳送給 print 函式。

觀察者模式的應用:資料格式化觀察器

在這個範例中,我們將實作觀察者模式,以觀察資料格式化的變化。首先,讓我們定義一個 Publisher 類別,負責管理觀察者並發布事件。

class Publisher:
    def __init__(self):
        self.observers = []

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

    def remove_observer(self, observer):
        self.observers.remove(observer)

    def notify_observers(self, data):
        for observer in self.observers:
            observer.update(data)

接下來,定義一個 DefaultFormatter 類別,負責格式化資料。這個類別將包含 data 屬性,並提供 getter 和 setter 方法。

class DefaultFormatter:
    def __init__(self, name):
        self.name = name
        self.data = 0

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, value):
        self._data = value
        print(f"{self.__class__.__name__}: '{self.name}' has data = {self._data}")

    def __str__(self):
        return f"{self.name}: {self.data}"

現在,讓我們定義兩個觀察者類別:HexFormatterObsBinaryFormatterObs。這些類別將繼承 DefaultFormatter 類別,並實作 update 方法,以便在資料變化時更新自己的狀態。

class HexFormatterObs(DefaultFormatter):
    def update(self, data):
        print(f"{self.__class__.__name__}: '{self.name}' has now hex data = {hex(data)}")

class BinaryFormatterObs(DefaultFormatter):
    def update(self, data):
        print(f"{self.__class__.__name__}: '{self.name}' has now bin data = {bin(data)}")

最後,讓我們實作主程式部分,建立一個 Publisher 例項,新增觀察者,並測試資料變化。

publisher = Publisher()
test1 = DefaultFormatter("test1")
hex_observer = HexFormatterObs("test1")
binary_observer = BinaryFormatterObs("test1")

publisher.add_observer(hex_observer)
publisher.add_observer(binary_observer)

test1.data = 15.8
publisher.notify_observers(int(test1.data))

test1.data = 3
publisher.notify_observers(test1.data)

test1.data = 21
publisher.notify_observers(test1.data)

執行這段程式碼後,我們可以觀察到觀察者模式的作用。當 test1data 屬性變化時,觀察者將自動更新自己的狀態,並列印預出相應的格式化資料。

圖表翻譯:

  flowchart TD
    A[Publisher] --> B[Observer]
    B --> C[DefaultFormatter]
    C --> D[HexFormatterObs]
    C --> E[BinaryFormatterObs]
    D --> F[Update Hex Data]
    E --> G[Update Binary Data]

內容解密:

在這個範例中,我們使用觀察者模式實作了資料格式化觀察器。當資料變化時,觀察者將自動更新自己的狀態,並列印預出相應的格式化資料。這個模式允許我們在不改變主程式碼的情況下新增新的觀察者和格式化邏輯。

觀察者模式的應用和實作

觀察者模式是一種設計模式,允許物件在執行時動態地新增或刪除觀察者,從而實作物件之間的解耦和靈活的溝通。這種模式在許多領域中都有廣泛的應用,例如 GUI 程式設計、事件驅動程式設計等。

觀察者模式的實作

觀察者模式的實作通常包括以下幾個步驟:

  1. 定義一個主題(Subject)介面,該介面負責管理觀察者列表和通知觀察者。
  2. 定義一個觀察者(Observer)介面,該介面負責定義觀察者需要實作的方法。
  3. 建立一個主題類,該類實作主題介面,並負責管理觀察者列表和通知觀察者。
  4. 建立一個觀察者類,該類實作觀察者介面,並負責實作觀察者需要實作的方法。

Python 中的觀察者模式實作

以下是一個簡單的 Python 實作:

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, modifier=None):
        for observer in self.observers:
            if modifier != observer:
                observer.update(self)

class Data(Subject):
    def __init__(self, name=''):
        super().__init__()
        self.name = name
        self._data = 0

    @property
    def data(self):
        return self._data

    @data.setter
    def data(self, value):
        self._data = value
        self.notify()

class HexFormatterObserver:
    def update(self, subject):
        print(f'HexFormatterObserver: {subject.name} has data 0x{subject.data:x}')

class BinaryFormatterObserver:
    def update(self, subject):
        print(f'BinaryFormatterObserver: {subject.name} has data {subject.data:b}')

def main():
    data1 = Data('Data 1')
    data2 = Data('Data 2')

    hex_formatter = HexFormatterObserver()
    binary_formatter = BinaryFormatterObserver()

    data1.attach(hex_formatter)
    data1.attach(binary_formatter)
    data2.attach(hex_formatter)
    data2.attach(binary_formatter)

    print("Setting Data 1 = 10")
    data1.data = 10
    print("Setting Data 2 = 15")
    data2.data = 15
    print("Setting Data 1 = 3")
    data1.data = 3
    print("Setting Data 2 = 5")
    data2.data = 5

    print("Detach hex_formatter from data1 and data2.")
    data1.detach(hex_formatter)
    data2.detach(hex_formatter)
    print("Setting Data 1 = 10")
    data1.data = 10
    print("Setting Data 2 = 15")
    data2.data = 15

if __name__ == '__main__':
    main()

觀察者模式的優點

觀察者模式有以下幾個優點:

  • 解耦: 觀察者模式可以實作物件之間的解耦,減少物件之間的依賴關係。
  • 靈活性: 觀察者模式可以在執行時動態地新增或刪除觀察者,增加了系統的靈活性。
  • 可擴充套件性: 觀察者模式可以方便地增加新的觀察者,增加了系統的可擴充套件性。

從軟體架構的演進來看,觀察者模式有效地解決了物件之間的緊密耦合問題。本文深入探討了觀察者模式的優點、實作方式以及應用場景,並提供了清晰的 Python 程式碼範例。分析程式碼可以發現,觀察者模式的核心在於定義清晰的主題和觀察者介面,以及實作觀察者註冊、移除和通知機制。儘管觀察者模式提升了程式碼的彈性和可維護性,但如果觀察者數量過多或通知頻率過高,可能會影響系統效能,需注意觀察者管理和通知策略的最佳化。展望未來,隨著事件驅動架構的普及,觀察者模式將在建構高度解耦和可擴充套件的系統中扮演更重要的角色,值得開發者深入理解和應用。對於構建回應式系統和需要實作事件驅動架構的專案,玄貓建議優先考慮採用觀察者模式。