大家好,今天和大家来聊一个新的设计模式——订阅者模式

这个模式在我们的生活当中非常常见,可以说是几乎所有的媒体平台都用或多或少地用到了这个模式。比如公众号,我们来仔细梳理一下公众号这个平台当中的整个逻辑,会发现其实这里面一共有三方存在,这三方呈一个三角关系。

三方订阅关系

画出来的话大概是这个样子:

这张图大家应该很好理解,TechFlow每天把新的文章发布到公众号平台上,平台会把内容推送给那些关注了TechFlow的用户,大家正是因为关注了TechFlow才读到了这篇文章。

所以在订阅制的内容平台当中,其实是一个三方关系,而不是读者和作者两方关系,平台在当中起到了媒介的作用。只是很多时候作为读者,我们可能会忽略平台的存在。

既然存在三方关系,我们在实现相关逻辑的时候就需要把这三方剥离出来,单独实现各自的逻辑,这样代码的耦合性才最低,可以更加方便以后的拓展。

代码实现

订阅者模式其实没有太多内容需要讲,我们只需要记住一点,就是尽量让属于不同实体的代码逻辑分开,而不是耦合在一起就可以了,没有太多其他的门道。

我们先来看这个模式当中最简单的部分,也就是Publisher(作者)这个部分。对于作者而言,它是最简单的,因为它能做的事情非常少,就只有发布内容而已。当然一般现在也会有一些作者保护机制,比如说可以拉黑一些不喜欢的用户什么的,但这里我们不需要考虑那么多,只需要考虑基本功能就行了。对于作者而言,最基本的功能就是发布内容。

代码非常简单,就只有几行。

class Publisher:

    def __init__(self, msg_center):
        self.provider = msg_center

    def publish(self, msg):
        self.provider.notify(msg)

因为作者自身是没办法发布内容的,作者是把要发布的内容上传到平台,平台代替作者去发布的。体现在这个类当中就是我们调用了provider也就是平台去执行通知(notify)的操作,把新发布的内容推送到读者端。

读者端稍微复杂一点,因为读者不仅可以订阅,还可以取关,并且收到了消息之后还可以打开以及一些互动。这里我们把功能简化,就留下了三个最基本的功能,分别是关注、取关以及操作。

class Subscriber:
    
    def __init__(self, name, msg_center):
        self.name = name
        self.provider = msg_center

    def subscribe(self, msg):
        self.provider.subscribe(msg, self)

    def unsubscribe(self, msg):
        self.provider.unsubscribe(msg, self)

    def run(self, msg):
        print('{} got {}'.format(self.name, msg))

从代码当中我们可以看到,读者端的操作其实也是和平台交互。平台是读者和作者之间的媒介,读者和作者之间不直接发生关联。这其实是非常不错的设计,如果关联和依赖很多,就会出现要开发新功能的时候畏手畏脚,会影响其他模块的情况发生。

最后,我们来看平台的部分,平台的部分其实也不复杂,只是用一个dict存储了读者和作者之间的订阅关系而已。其实这里没必要使用setdefault,使用defaultdict会更好。

class Provider:

    def __init__(self):
        self.msg_queue = []
        self.subscribers = {}

    def notify(self, msg):
        self.msg_queue.append(msg)

    def subscribe(self, msg, subscriber):
        self.subscribers.setdefault(msg, []).append(subscriber)

    def unsubscribe(self, msg, subscriber):
        self.subscribers[msg].remove(subscriber)

    def update(self):
        # 遍历所有的作者
        for msg in self.msg_queue:
            # 遍历所有订阅了msg的读者进行推送
            for sub in self.subscribers.get(msg, []):
                sub.run(msg)
        self.msg_queue = []

这里我把执行的测试代码也放上来,大家感兴趣可以自己试验一下。

if __name__ == '__main__':
    message_center = Provider()
    fftv = Publisher(message_center)

    jim = Subscriber('jim', message_center)
    jim.subscribe('cartoon')

    jack = Subscriber('jack', message_center)
    jack.subscribe('music')

    gee = Subscriber('gee', message_center)
    gee.subscribe('movie')

    vani = Subscriber('vani', message_center)
    vani.subscribe('movie')

    fftv.publish('cartoon')
    fftv.publish('music')
    fftv.publish('ads')
    fftv.publish('cartoon')
    fftv.publish('cartoon')
    fftv.publish('movie')
    fftv.publish('blank')

    message_center.update()

从代码层面来说,这个设计模式没有太多的难度,主要是一种解耦的思想。这一个思想很重要,在实际开发当中,我们决不能仅仅满足于实现产品经理的功能,因为产品经理往往是不懂这些系统之间架构和软件工程的。我们之所以需要设计模式只有30%的原因是为了更好的效率以及更简洁的代码,剩下70%的原因其实都是为了抵御日后功能需求的变化。

废话不多说了,今天的文章就到这里,衷心祝愿大家每天都有所收获。如果还喜欢今天的内容的话,请来一个三连支持吧~(点赞、关注、转发

订阅者模式,公众号、B站、快手用了都说好!的更多相关文章

  1. 利用python 实现微信公众号群发图片与文本消息功能

    在微信公众号开发中,使用api都要附加access_token内容.因此,首先需要获取access_token.如下: #获取微信access_token def get_token(): paylo ...

  2. 微信公众号token的asp.net脚本

    老板让我搞一个微信公众号.好吧.前面都很EZ,直到要使用一个token验证服务器的有效性. 看了下文档,大概意思就是微信的服务器用GET请求访问你的服务器. 其中包含了signature,nonce, ...

  3. 通过微信公众号API复制公众号自定义菜单同时增加子菜单方法

    主要的原因是再不破坏公众号以前的菜单的基础上增加自定义菜单,主要步骤如下: 1.通过微信提供的微信公众平台接口调试工具获取公众号的所有自定义菜单 网址:https://mp.weixin.qq.com ...

  4. 微信公众号ID也可以修改了!

    差不多一年前,微信团队宣布个人类帐号一个自然年内可主动修改两次名称,那一天大家奔走相告纷纷修改成自己早就心仪的名字,有人猛然发现公众号名字改了,可ID还是xiaopipi(小屁屁)这可如何是好,洗不去 ...

  5. [转]微信小程序、微信公众号、H5之间相互跳转

    本文转自:https://www.cnblogs.com/colorful-paopao1/p/8608609.html 转自慕课网 一.小程序和公众号 答案是:可以相互关联. 在微信公众号里可以添加 ...

  6. 微信小程序、微信公众号、H5之间相互跳转

    转自慕课网 一.小程序和公众号 答案是:可以相互关联. 在微信公众号里可以添加小程序. 图片有点小,我把文字打出来吧: 可关联已有的小程序或快速创建小程序.已关联的小程序可被使用在自定义菜单和模版消息 ...

  7. python微信公众号开发学习记录

    网上有很多微信公众号的开发教程,但是都是好几年前的了,而且很多都是抄袭其他人的,内容几乎一模一样.真的无语了.只好自己总结一下开发的一些简单流程. 一先去注册个微信公众号,这个就不详细说了, 二登录后 ...

  8. 微信公众号获取微信token

    微信在公众号和小程序的开发都有开放文档一般看文档开发就行,很简单这里写一个小demo获取微信token,之后根据自己的业务获取信息处理即可 package com.demo.ccx; import o ...

  9. 微信公众号添加zip

    微信公众号添加zip的教程 我们都知道创建一个微信公众号,在公众号中发布一些文章是非常简单的,但公众号添加附件下载的功能却被限制,如今可以使用小程序“微附件”进行在公众号中添加附件. 以下是公众号添加 ...

  10. 微信公众号里放XLS链接

    微信公众号里放XLS链接 我们都知道创建一个微信公众号在公众号中发布一些文章是非常简单的,但公众号添加附件下载的功能却被限制,如今可以使用小程序“微附件”进行在公众号中添加附件,如:xls,word等 ...

随机推荐

  1. Elasticsearch 学习二(请求流程).

    一.写入数据 1.ES 的任意节点都可以作为协调(Coordinating)节点接受请求(包括新建.索引或者删除请求),每个节点都知道集群中任一文档位置: 2.协调节点会通过 routing 字段计算 ...

  2. NET 5 Cron表达式

    cron表达式通过特定的规则指定时间,用于定时任务 1. 整体结构 cron表达式是一个字符串,分为6或7个域,每两个域之间用空格分隔,其语法格式为: "秒域 分域 时域 日域 月域 周域 ...

  3. EF快速入门--直接修改(简要介绍ObjectContext处理机制)

    原博文 http://www.cnblogs.com/fly_dragon/archive/2011/06/05/2073084.html ObjectContext的处理机制 ObjectConte ...

  4. Linux嵌入式学习-ds18b20驱动

    ds18b20的时序图如下: 复位时序: 读写时序: 以下是程序代码: #include <linux/module.h> #include <linux/init.h> #i ...

  5. Linux介绍及系统安装

    1.Linux入门介绍 1.1简介 ​ Linux,全称GNU/Linux,是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX(Portable Operating System In ...

  6. android基本组件 Button

    android中提供了普通按钮Buttton和图片按钮ImageButton两种按钮组件,ImageButton按钮中主要有一个android:src属性,用于设置按钮的背景图片.可以在Button的 ...

  7. Spark学习进度-Transformation算子

    Transformation算子 intersection 交集 /* 交集 */ @Test def intersection(): Unit ={ val rdd1=sc.parallelize( ...

  8. Spring(2) --Bean相关

    你对Spring中的bean了解吗?都有哪些作用域(Scope)? Spring 官方文档对 bean 的解释是: In Spring, the objects that form the backb ...

  9. 数据仓库组件:HBase集群环境搭建和应用案例

    本文源码:GitHub || GitEE 一.Hbase简介 1.基础描述 Hadoop原生的特点是解决大规模数据的离线批量处理场景,HDFS具备强大存储能力,但是并没有提供很强的数据查询机制.HBa ...

  10. linux kernel操作GPIO函数

    一.头文件 #include <asm/gpio.h> 二.注册 GPIO int gpio_request(unsigned gpio, const char *label) 功能:申请 ...