订阅者模式,公众号、B站、快手用了都说好!
大家好,今天和大家来聊一个新的设计模式——订阅者模式。
这个模式在我们的生活当中非常常见,可以说是几乎所有的媒体平台都用或多或少地用到了这个模式。比如公众号,我们来仔细梳理一下公众号这个平台当中的整个逻辑,会发现其实这里面一共有三方存在,这三方呈一个三角关系。
三方订阅关系
画出来的话大概是这个样子:

这张图大家应该很好理解,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站、快手用了都说好!的更多相关文章
- 利用python 实现微信公众号群发图片与文本消息功能
在微信公众号开发中,使用api都要附加access_token内容.因此,首先需要获取access_token.如下: #获取微信access_token def get_token(): paylo ...
- 微信公众号token的asp.net脚本
老板让我搞一个微信公众号.好吧.前面都很EZ,直到要使用一个token验证服务器的有效性. 看了下文档,大概意思就是微信的服务器用GET请求访问你的服务器. 其中包含了signature,nonce, ...
- 通过微信公众号API复制公众号自定义菜单同时增加子菜单方法
主要的原因是再不破坏公众号以前的菜单的基础上增加自定义菜单,主要步骤如下: 1.通过微信提供的微信公众平台接口调试工具获取公众号的所有自定义菜单 网址:https://mp.weixin.qq.com ...
- 微信公众号ID也可以修改了!
差不多一年前,微信团队宣布个人类帐号一个自然年内可主动修改两次名称,那一天大家奔走相告纷纷修改成自己早就心仪的名字,有人猛然发现公众号名字改了,可ID还是xiaopipi(小屁屁)这可如何是好,洗不去 ...
- [转]微信小程序、微信公众号、H5之间相互跳转
本文转自:https://www.cnblogs.com/colorful-paopao1/p/8608609.html 转自慕课网 一.小程序和公众号 答案是:可以相互关联. 在微信公众号里可以添加 ...
- 微信小程序、微信公众号、H5之间相互跳转
转自慕课网 一.小程序和公众号 答案是:可以相互关联. 在微信公众号里可以添加小程序. 图片有点小,我把文字打出来吧: 可关联已有的小程序或快速创建小程序.已关联的小程序可被使用在自定义菜单和模版消息 ...
- python微信公众号开发学习记录
网上有很多微信公众号的开发教程,但是都是好几年前的了,而且很多都是抄袭其他人的,内容几乎一模一样.真的无语了.只好自己总结一下开发的一些简单流程. 一先去注册个微信公众号,这个就不详细说了, 二登录后 ...
- 微信公众号获取微信token
微信在公众号和小程序的开发都有开放文档一般看文档开发就行,很简单这里写一个小demo获取微信token,之后根据自己的业务获取信息处理即可 package com.demo.ccx; import o ...
- 微信公众号添加zip
微信公众号添加zip的教程 我们都知道创建一个微信公众号,在公众号中发布一些文章是非常简单的,但公众号添加附件下载的功能却被限制,如今可以使用小程序“微附件”进行在公众号中添加附件. 以下是公众号添加 ...
- 微信公众号里放XLS链接
微信公众号里放XLS链接 我们都知道创建一个微信公众号在公众号中发布一些文章是非常简单的,但公众号添加附件下载的功能却被限制,如今可以使用小程序“微附件”进行在公众号中添加附件,如:xls,word等 ...
随机推荐
- 自适应查询执行:在运行时提升Spark SQL执行性能
前言 Catalyst是Spark SQL核心优化器,早期主要基于规则的优化器RBO,后期又引入基于代价进行优化的CBO.但是在这些版本中,Spark SQL执行计划一旦确定就不会改变.由于缺乏或者不 ...
- Selenium Web元素定位方法
Selenium是用于Web应用测试的自动化测试框架,可以实现跨浏览器和跨平台的Web自动化测试.Selenium通过使用WebDriver API来控制web浏览器,每个浏览器都都有一个特定的Web ...
- os模块和os.path模块常用方法
今天和大家分享python内置模块中的os模块和os.path模块. 1.什么是模块呢? 在计算机开发过程中,代码越写越多,也就越来越难以维护,所以为了可维护的代码,我们会把函数进行分组,放在不同的文 ...
- bootstrap(一)栅格系统
中文网:http://www.bootcss.com/ 官网:http://v3.bootcss.com/ 需要准备:离线手册 和 软件包 项目中引用bootstrap.min.js压缩版和boo ...
- web基础知识,
# web基础 网上冲浪 surfing the Internet weibo.com 域名,主机名,微博服务器的地址名 当用户在地址栏输入一个URL(uniform resource,locator ...
- 6. 抹平差异,统一类型转换服务ConversionService
目录 ✍前言 版本约定 ✍正文 ConverterRegistry ConversionService ConfigurableConversionService GenericConversionS ...
- 配置简单的拦截器java中
springMVC.xml文件中==== <!-- 拦截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:map ...
- [LeetCode]234. Palindrome Linked List判断回文链表
重点是: 1.快慢指针找到链表的中点.快指针一次走两步,慢指针一次走一步,分清奇偶数情况. 2.反转链表.pre代表已经反转好的,每次将当前节点指向pre /* 快慢指针得到链表中间,然后用206题方 ...
- Tiny6410烧入uboot,linux内核,文件系统
好久没有玩tiny6410了,今天拿出来试试.之前学习一直是跟着视频学习的.今天自己动手来做一下. 首先我将光盘linux目录下的linux-2.6.38-20150708.tgz rootfs_r ...
- 事务的概念,以及事务在JDBC编程中处理事务的步骤
事务是作为单个逻辑工作单元执行的一系列操作,一个逻辑工作单元必须有四个属性,称为原子性.一致性.隔离性和持久性 (ACID) 属性,只有这样才能成为一个事务 .JDBC处理事务有如下操作: 1,con ...