观察者模式

107 阅读4分钟

1. 一句话简介

观察者模式(Observer Pattern)是一种行为型的设计模式,它定义了一种类似“发布-订阅”的机制,使得一个对象(通常称为它主题、观察目标、发布方)的状态发生变化时,能够通知依赖该对象的其他对象(一般称它们为观察者、订阅方)并让她们更新各自的状态。

2. 用一个例子来理解观察者模式:学校整点报时

小明所在的学校有一个时钟(观察目标),每到整点时,它就会通知所有的学生(观察者)当前的时间,咱们可以使用观察者模式实现这个时钟通知系统。注意点:时间从 0 开始,并每隔一个小时更新一次。

2.1 UML图例

classDiagram
    class Student {
        -name: string
        +update(message: Any): void
    }
    
    class IClock {
        <<interface>>
        +addStudent(student: Student): void
        +removeStudent(student: Student): void
        +notifyStudents(void): void
    }
    
    class HourlyClock {
        -now: int
        -students: List[Student]
        +addStudent(student: Student): void
        +removeStudent(student: Student): void
        +notifyStudents(void): void
        +tellTime(time: int): void
    }
    
    IClock <|.. HourlyClock
    HourlyClock o-- Student

2.2 Python代码

卡码网设计模式习题集【设计模式专题之观察者模式】13. 时间观察者的题解:

from abc import ABC, abstractmethod
from typing import Any


class Student:
    def __init__(self, name: str):
        self.name = name
        
    def update(self, message: Any):
        print(f"{self.name} {message}")


class IClock(ABC):
    @abstractmethod
    def addStudent(self, student: Student):
        raise NotImplemented
        
    @abstractmethod
    def removeStudent(self, student: Student):
        raise NotImplemented
        
    @abstractmethod
    def notifyStudents(self):
        raise NotImplemented
        
        
class HourlyClock(IClock):
    def __init__(self):
        self.now = 0
        self.students: List[str] = []
        
    def addStudent(self, student: Student):
        self.students.append(student)
        
    def removeStudent(self, student: Student):
        self.students.remove(student)
        
    def notifyStudents(self):
        for stu in self.students:
            stu.update(self.now)
            
    def tellTime(self, time: int):
        self.now = time
        self.notifyStudents()
        
        
def client():
    clock = HourlyClock()
    n = int(input())
    for _ in range(n):
        name = input()
        student = Student(name)
        clock.addStudent(student)
    m = int(input())
    for i in range(1, m + 1):
        clock.tellTime(i)
        
        
if __name__ == "__main__":
    client()

3. 观察者模式的结构

观察者模式的主体是观察主题和观察者,但是一般有5个组成部分:

  • 主题Subject, 一般会定义成一个接口,提供方法用于注册、删除和通知观察者,通常也包含一个状态,当状态发生改变时,通知所有的观察者。
  • 观察者Observer: 观察者也需要实现一个接口,包含一个更新方法,在接收主题通知时执行对应的操作。
  • 具体主题ConcreteSubject: 主题的具体实现, 维护一个观察者列表,包含了观察者的注册、删除和通知方法。
  • 具体观察者ConcreteObserver: 观察者接口的具体实现,每个具体观察者都注册到具体主题中,当主题状态变化并通知到具体观察者,具体观察者进行处理。
  • 客户端Client: 负责创建具体主题以及具体观察者,并调用具体主题的通知方法。

4. 观察者模式的优缺点以及使用场景

观察者模式是一种常用的设计模式,它具有许多优点,同时也有一些缺点。以下是观察者模式的优缺点以及适用场景:

优点:

  1. 松耦合:主题和观察者之间是松耦合的,主题只知道观察者接口,不需要知道具体的观察者,从而使系统更灵活、可扩展。

  2. 可重用性:主题和观察者可以被多次重复使用,不同的主题可以共享相同的观察者。

  3. 扩展性:可以在运行时动态地添加、删除观察者,而不需要修改主题的代码,从而实现了更好的扩展性。

  4. 灵活性:可以在不同的场景中使用观察者模式,例如在单线程或多线程环境下,同步或异步地通知观察者。

缺点:

  1. 可能引起内存泄漏:如果观察者没有被正确地移除,可能会导致内存泄漏问题,因为主题会保持对观察者的引用,即使观察者不再需要。

  2. 通知顺序不确定:观察者模式没有明确规定观察者被通知的顺序,这可能会导致观察者收到通知的顺序与期望不符。

  3. 可能导致性能问题:如果观察者数量较大,通知所有观察者可能会导致性能问题,特别是在多线程环境下。

使用场景:

  1. 事件处理:GUI 开发中的事件处理、用户界面更新等场景。

  2. 消息中间件:订阅/发布模式常用于消息中间件中,消息的发布者称为主题,消息的订阅者称为观察者。

  3. 状态变化通知:当一个对象的状态发生变化时,需要通知其他对象更新状态。

  4. 分布式系统:在分布式系统中,可以使用观察者模式来实现服务之间的通信和状态同步。

总的来说,观察者模式适用于一对多的依赖关系,当一个对象的改变需要通知其他多个对象时,观察者模式是一种有效的设计模式。

5. 参考文章