设计模式:第二章 观察者模式

第一章 前言
第二章 观察者模式
第三章 状态模式
第四章 中介模式
第五章 装饰器模式
第六章 单例模式
第七章 克隆模式
第八章 职责链模式
第九章 代理模式
第十章 外观模式
第十一章 迭代模式
第十二章 组合模式
第十三章 构建模式
第十四章 适配模式
第十五章 策略模式
第十六章 简单工厂模式
第十七章 工厂方法模式
第十八章 抽象工厂模式
第十九章 命令模式
第二十章 备忘录模式
第二十一章 享元模式
第二十二章 访问模式
第二十三章 模板模式
第二十四章 桥接模式
第二十五章 解释器模式
第二十六章 过滤器模式
第二十七章 对象池技术
第二十八章 回调机制
第二十九章 MVC模式
附录



1

1. 核心思想

监听模式又名观察者模式,顾名思义就是观察与被观察的关系。比如你在烧开水的时候看着它开没开,你就是观察者,水就是被观察者;再比如你在带小孩,你关注他是不是饿了,是不是渴了,是不是撒尿了,你就是观察者,小孩就是被观察者。观察者模式是对象的行为模式,又叫发布/订阅(Publish/Subscribe)模式,模型/视图(Model/View)模式,源/监听器(Source/Listener)模式或从属者(Dependents)模式。

监听模式是一种一对多的关系,可以有任意个(一个或多个)观察者对象同时监听某一个对象。监听的对象叫观察者(后面提到监听者,其实就指观察者,两者是相同的),被监听的对象叫被观察者(Observable,也叫主题,即Subject)。被观察者对象在状态或内容(数据)发生变化时,会通知所有观察者对象,使它们能够做出相应的变化(如自动更新子集的信息)。

观察者模式的核心思想就是在被观察者与观察者之间建立一种自动触发的关系。

2. UML类图

观察者模式UML类图

3. 代码框架

from abc import ABC, abstractmethod
from typing import Any


class Observer(ABC):
    """观察者基类

    Args:
        ABC (abc.ABCMeta): 申明为抽象基类
    """
    @abstractmethod
    def update(self, subject):
        """根据主题状态更新自己接口

        Args:
            subject (Subject): 订阅的主题类
        """
        pass


class Subject(ABC):
    """主题基类

    Args:
        ABC (abc.ABCMeta): 申明为抽象基类
    """

    def __init__(self, observers: "list[Observer]" = [], state: Any=None) -> None:
        """主题基类初始化函数

        Args:
            observers (list[Observer], optional): 订阅该主题观察者列表. Defaults to [].
            state (Any, optional): 主题初始化状态. Defaults to None.
        """
        self.__observers = observers
        self.__state = state

    def addObserver(self, observer: Observer):
        """为主题添加观察者

        Args:
            observer (Observer): 待添加的观察者
        """
        self.__observers.append(observer)

    def removeObserver(self, observer: Observer):
        """为主题移除观察者

        Args:
            observer (Observer): 待移除的观察者
        """
        self.__observers.remove(observer)

    def getState(self):
        """获取subject状态

        Returns:
            Any: Subject此时的状态
        """
        return self.__state

    def notify(self):
        """通知订阅该主题的观察者
        """
        for o in self.__observers:
            o.update(self)

    @abstractmethod
    def change(self, state):
        """subject状态改变

        Args:
            state (Any): subject状态
        """
        self.__state = state


class ObserverImplA(Observer):
    """观察者实现类A

    Args:
        Observer (Class): 观察者基类
    """
    def update(self, subject:  Subject):
        """根据subject传来的state,给出不同的操作

        Args:
            subject (Subject): 订阅的主题
        """
        if subject.getState() == "A":
            print(f"{self.__class__.__name__} is updating...")


class ObserverImplB(Observer):
    """观察者实现类B

    Args:
        Observer (Class): 观察者基类
    """
    def update(self, subject: Subject):
        """根据subject传来的state,给出不同的操作

        Args:
            subject (Subject): 订阅的主题
        """
        if subject.getState() == "B":  # 根据state状态,做出相应的动作
            print(f"{self.__class__.__name__} is updating...")

1
class SubjectImpl(Subject):
    """主题具体实现类

    Args:
        Subject (Class): 主题抽象基类
    """
    def change(self, state):
        """改变主题状态

        Args:
            state (Any): 待转换的状态
        """
        super().change(state)


if __name__ == "__main__":
    observerA = ObserverImplA()
    observerB = ObserverImplB()
    subject = SubjectImpl(observers=[observerA, observerB])

    subject.change(state="A")
    subject.notify()
    print("*" * 35)
    subject.change(state="B")
    subject.notify()

4. 模型说明

4.1 设计要点

在设计监听模式的程序时要注意以下几点:

  1. 要明确谁是观察者谁是被观察者,只要明白谁是应该关注的对象,问题也就明白了。一般观察者与被观察者之间是多对一的关系,一个被观察对象可以有多个监听对象(观察者)。如一个编辑框,有鼠标点击的监听者,也有键盘的监听者,还有内容改变的监听者。
  2. Observable 在发送广播通知的时候,无须指定具体的 Observer,Observer可以自己决定是否订阅Subject的通知。
  3. 被观察者至少需要有三个方法:添加监听者、移除监听者、通知Observer的方法。观察者至少要有一个方法:更新方法,即更新当前的内容,做出相应的处理。
  4. 添加监听者和移除监听者在不同的模型称谓中可能会有不同命名,如在观察者模型中一般是addObserver/removeObserver;在源/监听器(Source/Listener)模型中一般是attach/detach,应用在桌面编程的窗口中还可能是attachWindow/detachWindow或Register/Unregister。不要被名称弄迷糊了,不管它们是什么名称,其实功能都是一样的,就是添加或删除观察者。

4.2 推模型和拉模型

监听模式根据其侧重的功能还可以分为推模型和拉模型。推模型和拉模型其实更多的是语义和逻辑上的区别。

  • 推模型:被观察者对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。一般在这种模型的实现中,会把被观察者对象中的全部或部分信息通过update参数传递给观察者(update(Objectobj),通过obj参数传递)。如某App的服务要在凌晨1:00开始进行维护,1:00—2:00所有服务会暂停,这里你就需要向所有的App客户端推送完整的通知消息:“本服务将在凌晨1:00开始进行维护,1:00—2:00所有服务会暂停,感谢您的理解和支持!”不管用户想不想知道,也不管用户会不会在这期间访问App,消息都需要被准确无误地发送到。这就是典型的推模型的应用。
  • 拉模型:被观察者在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到被观察者对象中获取,相当于观察者从被观察者对象中拉数据。一般在这种模型的实现中,会把被观察者对象自身通过 update 方法传递给观察者(update(Observable observable),通过observable 参数传递),这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。如某App有新的版本推出,需要发送一个版本升级的通知消息,而这个通知消息只会简单地列出版本号和下载地址,如果需要升级App,还需要调用下载接口去下载安装包完成升级。这其实也可以理解成拉模型。

4.3 优缺点

  1. 优点

    • 观察者和被观察者是抽象耦合的。
    • 建立一套触发机制。
  2. 缺点

    • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
    • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
    • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

5. 应用场景

  1. 对一个对象状态或数据的更新需要其他对象同步更新,或者一个对象的更新需要依赖另一个对象的更新。
  2. 对象仅需要将自己的更新通知给其他对象而不需要知道其他对象的细节,如消息推送。

[1]程杰.大话数据模式[M].北京:清华大学出版社,2007.
[2]罗伟富.人人都懂设计模式:从生活中领悟设计模式:Python实现[M].北京:电子工业出版社,2019.


  1. 本文为书籍《大话数据模式》和《人人都懂设计模式:从生活中领悟设计模式:Python实现》阅读笔记 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值