观察者模式(Observer)及代码实现

模式定义:

观察者模式 实现了 发布-订阅 机制,在一个对象发生改变时,主动通知其他 一个或多个对象进行对应操作;

 

生活中的例子

  • 店铺上新款,发送邮件给用户;  店铺上新款 是 发布者, 订阅者是 用户;
  • 血汗工厂 下班时 的铃声,通知 所有员工 下班; 铃声 是 发布者, 订阅者是 所有员工;

 

代码中的例子(何时该使用此模式):

  • 基于AMQP协议实现的 rabbitmq 的 广播模式;一个消息发布者 对应多个消息消费者

 

生活中演化步骤

手机出新品时,用户想去 当地手机体验店 体验一下新机,但是 最近几天货还没到, 怎么办呢? 

1.用户可以 每天去体验店 问一下到货了吗,这样来回去了10天 才到货,人也瘦了10斤; 缺点:用户轮询,浪费资源

2.用户可以 把自己手机号给体验店员工,当到货时 给你发短信; 优点:用户 异步非阻塞(用户可以做其他事情,等发短信时再去体验新机),可以通过此 观察者模式实现;

 

该模式关键的角色:

  • 抽象发布者角色:定义子类的公共 注册,删除 观察者的方法,及通知所有观察者的方法;
  • 具体发布者角色:实现 注册,删除,通知 观察者的具体功能;
  • 抽象订阅者角色:定义子类的 功能方法;注意此方法名称 所有子类必须相同;
  • 具体订阅者角色:实现 具体的功能方法;此方法 在具体订阅者角色的所有类中必须相同;

 

该模式的主要优缺点如下:

优点:

  • 开闭原则; 无需修改发布者代码就能引入新的订阅者类 (如果是发布者接口则可轻松引入发布者类)
  • 降低了 发布者和订阅者之间,订阅者 彼此之间 的耦合;
  • 发布者和订阅者之间建立了一种 触发机制;发布者主动触发订阅者, 这种异步非阻塞(回调) 的 方式效率 相对于 同步非阻塞(轮询) 的 方式高很多;

缺点:

  • 发布者 通知所有订阅者时 由于订阅者被注册在list中,所以限制了 并发性,且强制增加了 订阅者间的执行顺序; 可以根据实际情况 在通知订阅者时,使用 多进程或多线程 摆脱此缺点;

 

 

和 其他模式 的 比较:

责任链模式 比较:

相同点:

       责任链模式中的责任者,观察者模式中的订阅者 在处理请求时, 客户端都可以定义 处理顺序; 因为 观察者模式中,发布者用list存储订阅者;

不同点:

       责任链模式 在客户端设置完请求顺序后,无法修改; 而 观察者模式 中的 观察者可以动态的  添加或删除订阅者;

在一些需求 如 一个请求 只需要按照一定的顺序 让每个处理者 处理时, 这2种模式都可以实现(但是最好还是用 责任者模式,因为 发布订阅模式 订阅者最好只接收来自发布者的原始消息,而不是其他订阅者处理完修改后的消息),这时模式的区别只在于 责任链模式中 定义下一个处理者的位置在上个处理者, 观察者模式中 定义订阅者 在观察者角色 list类型的类变量中;

 

示例代码部分

# -*- coding: utf-8 -*-
"""
(C) rgc
All rights reserved
create time '2020/12/10'

Usage:
观察者模式 实现示例

一个应用 实现 股票涨停 时, 邮件和短信通知 用户
这时 股票涨停 是事件发布者,事件订阅者 是 邮件通知功能 和 短信通知功能
"""
import random


class BasePublisher(object):
    """

    """

    def add(self, subscriber):
        """

        :param subscriber:
        :return:
        """
        pass

    def cancel(self, subscriber):
        """

        :param subscriber:
        :return:
        """
        pass

    def notify(self, context):
        """

        :param context:
        :return:
        """
        pass


class Publisher(BasePublisher):
    """

    """
    # 订阅者list
    subscriber_list = []

    def add(self, subscriber):
        """

        :param subscriber:
        :return:
        """
        self.subscriber_list.append(subscriber)

    def cancel(self, subscriber):
        """

        :param subscriber:
        :return:
        """
        self.subscriber_list.remove(subscriber)

    def notify(self, context):
        """

        :param context:
        :return:
        """
        for item in self.subscriber_list:
            item.send(context)

    def check_trading_limit(self, user_name):
        """
        检查是否涨停
        此处用 随机数表示是否涨停
        :param user_name:
        :return:
        """
        # 1表示涨停
        if random.randint(0, 1):
            self.notify(user_name)
        else:
            print('股票没有涨停,不发通知')


class BaseSubscriber(object):
    """

    """

    def send(self, name):
        """

        :return:
        """
        pass


class EmailSubscriber(BaseSubscriber):
    """
    邮件订阅者角色 类
    """

    def send(self, name):
        """

        :return:
        """
        print(f'hello,{name},现在发送邮件通知您 的股票涨停了!!!')


class SmsSubscriber(BaseSubscriber):
    """
    短信订阅者角色 类
    """

    def send(self, name):
        """

        :return:
        """
        print(f'hello,{name},现在发送短信通知您 的股票涨停了!!!')


if __name__ == '__main__':
    # 初始化 发布者,订阅者 对象
    pub = Publisher()
    email_sub = EmailSubscriber()
    sms_sub = SmsSubscriber()
    # 发布者 添加订阅者
    pub.add(email_sub)
    pub.add(sms_sub)
    # 发布者 触发事件
    pub.check_trading_limit('Lucy')
    pub.check_trading_limit('Tom')

    # 发布者 删除 订阅者
    pub.cancel(email_sub)

    print('*' * 10)
    pub.check_trading_limit('Jerry')
    pub.check_trading_limit('Bob')

示例运行结果:

 

总结: 

观察者模式 一定程度实现了 异步非阻塞的 功能,通过触发式 的 执行任务,而不是轮询 从而节约资源;

观察者模式 和 责任链模式 有一定程度的相似性,如果不考虑彼此不同点,可以都试试;

但是 观察者侧重于 发布订阅消息,每个订阅者接收的消息应该相同,且处理消息时应该无序; 责任链 注重任务处理顺序的灵活配置;

 

相关链接:

http://c.biancheng.net/view/1390.html

https://refactoringguru.cn/design-patterns/observer

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值