专栏首页平头哥的技术博文观察者模式,从公众号群发说起
原创

观察者模式,从公众号群发说起

每个人应该都订阅了不少微信公众号,那你有没有注意到微信公众号的消息呢?你订阅的公众号号主每发布一篇文章,你都会主动的接收到文章的推送,并不需要你点开每个订阅的公众号一一查看有没有更新,是不是觉得有点意思?感兴趣?那就接着往下看吧,因为接下来我们要模拟公众号群发的场景。

要模拟公众号群发,首先需要简单的了解一下公众号的特点,对于公众号的特点,我总结了以下三点:

  • 每个公众号会有多名订阅者,公众号跟订阅者在某种层面上是一对多的关系
  • 只有订阅者才能在公众号发布新文章时,会及时接收到推送通知,没有订阅公众号的阅读者不会接收到文章推送通知。
  • 每个订阅者都依赖于公众号号主,只有公众号号主发布文章,订阅者才有文章查看

现在业务场景我们大概知道了,那就开始动手编写我们的业务吧,我们先从公众号号主开始。

对于公众号号主,我们先理解一下公众号特点的第二点:只有订阅者才能在公众号发布新文章时,会及时接收到推送通知,没有订阅公众号的阅读者不会接收到文章推送通知。这个特点说明在公众号号主这边维护者订阅者的列表,在每次发布文章时会通知列表中的每一个订阅者告诉他们有新文章了。如果号主没有订阅者列表,那怎么知道需要通知哪些人呢?对于这个订阅者列表,号主肯定有增删的权利,毕竟这个公众号你说了算。根据上面分析的,我们给号主做出一个抽象,我们建立一个抽象的Author类。Author类具体设计如下:

/**
 * 号主抽象类
 */
public interface Author {
    // 添加关注者
    void addReader(Reader reader);
    // 删除关注者
    void deleteReader(Reader reader);
    // 通知关注者
    void notifyReader();
    // 写文章
    void writeArticle(String article);
}

在我们的场景中号主主要有添加订阅者、删除订阅者、通知订阅者和写文章的功能,号主有了,接下来就是我们的订阅者,订阅者在我们的场景中就比较简单了,只有一个阅读的功能,我们定义一个阅读者抽象类ReaderReader类的具体设计如下:

/**
 * 阅读者接口
 */
public interface Reader {
    // 阅读文章
    void reader(String authorName,String article);
}

号主和阅读者的接口都定义好了,下面我们就需要真正的号主和订阅者了。我们建立我们第一个号主平头哥PingtougeAuthorPingtougeAuthor类的设计如下:

/**
 * @author 平头哥
 * @title: PingtougeAuthor
 * @projectName observer
 * @description: 号主平头哥
 * @date 2019/9/1817:50
 */
public class PingtougeAuthor implements Author{
    // 订阅者列表
    private Vector<Reader> readers ;
    // 作者名称
    private String name;
    // 文章
    private String article;

    public PingtougeAuthor(String name){
        this.name = name;
        this.readers = new Vector<>();
    }
    /**
     * 添加关注者
     * @param reader
     */
    @Override
    public void addReader(Reader reader) {
        if (readers.contains(reader)) return;
        readers.add(reader);
    }
    /**
     * 移除关注者
     * @param reader
     */
    @Override
    public void deleteReader(Reader reader) {
        readers.remove(reader);
    }
    /**
     * 通知关注者
     */
    @Override
    public void notifyReader() {
        for (Reader reader:readers){
            reader.reader(name,article);
        }
    }
    /**
     * 写文章
     * @param article
     */
    @Override
    public void writeArticle(String article){
        this.article = article;
        notifyReader();
    }
}

我们在建立王山、张三、李四三个订阅者,他们的具体设计如下:

/**
 * 订阅者王山
 */
public class WangSanReader implements Reader{
    private String name;
    public WangSanReader(String name){
        this.name = name;
    }
    @Override
    public void reader(String authorName,String article) {
        System.out.println(name+" 阅读了 "+authorName+" 发布的 "+article+" 文章");
    }
}
/**
 * 订阅者张三
 */
public class ZhangsanReader implements Reader{
    private String name;

    public ZhangsanReader(String name){
        this.name = name;
    }

    @Override
    public void reader(String authorName,String article) {
        System.out.println(name+" 阅读了 "+authorName+" 发布的 "+article+" 文章");
    }
}
/**
 * 订阅者者李四
 */
public class LiSiReader implements Reader{
    private String name;

    public LiSiReader(String name){
        this.name = name;
    }
    @Override
    public void reader(String authorName,String article) {
        System.out.println(name+" 阅读了 "+authorName+" 发布的 "+article+" 文章");
    }
}

号主和订阅者都有了,万事俱备只欠东风,那我们就来进行我们第一次场景模拟,在这次模拟中,订阅者张三、王山订阅了平头哥的公众号,李四没有订阅平头哥的公众号。按照我们的设想平头哥发布文章时,张三、王山可以接收到文章推送通知,李四不会接收到文章推送通知。编写我们的第一个模拟类:

public class App {
    public static void main(String[] args) {
        PingtougeAuthor pingtougeAuthor = new PingtougeAuthor("平头哥");
        
        WangSanReader wangSanReader = new WangSanReader("王山");
        ZhangsanReader zhangsanReader = new ZhangsanReader("张三");
        LiSiReader liSiReader = new LiSiReader("李四");
        
        // 王山订阅了平头哥的公众号
        pingtougeAuthor.addReader(wangSanReader);
        // 张三订阅了平头哥的公众号
        pingtougeAuthor.addReader(zhangsanReader);
        
        pingtougeAuthor.writeArticle("看完这篇你还不知道这些队列,我这些图白作了");
    }
}

测试结果:

2019-09-18 20-05-03屏幕截图.png

从测试结果中,我们可以看出张三、王山接收到了平头哥发布文章的推送通知,李四没有接收到,符合我们的预期。

接下来我们进行第二次模拟,由于平头哥最近发了不少干货,李四决定也关注平头哥的公众号,所以我们对模拟类进行了修改,修改后的模拟类如下:

public class App {
    public static void main(String[] args) {
        PingtougeAuthor pingtougeAuthor = new PingtougeAuthor("平头哥");
        WangSanReader wangSanReader = new WangSanReader("王山");
        ZhangsanReader zhangsanReader = new ZhangsanReader("张三");
        LiSiReader liSiReader = new LiSiReader("李四");

        // 王山订阅了平头哥的公众号
        pingtougeAuthor.addReader(wangSanReader);
        // 张三订阅了平头哥的公众号
        pingtougeAuthor.addReader(zhangsanReader);
//
//        pingtougeAuthor.writeArticle("看完这篇你还不知道这些队列,我这些图白作了");
        // 李四也订阅平头哥的公众号
        pingtougeAuthor.addReader(liSiReader);

        pingtougeAuthor.writeArticle("实现 Java 本地缓存,该从这几点开始");

    }
}

测试结果:

2019-09-18 20-12-56屏幕截图.png

这次三个订阅者都接收到了平头哥发布文章的推送通知。关注了平头哥的公众号有一段时间后,张三觉得平头哥的公众号不适合自己,于是就取关了平头哥的公众号,这是我们要模拟的第三个场景,我们对模拟类进行修改,修改后的模拟类如下:

public class App {
    public static void main(String[] args) {
        PingtougeAuthor pingtougeAuthor = new PingtougeAuthor("平头哥");
        WangSanReader wangSanReader = new WangSanReader("王山");
        ZhangsanReader zhangsanReader = new ZhangsanReader("张三");
        LiSiReader liSiReader = new LiSiReader("李四");

        // 王山订阅了平头哥的公众号
        pingtougeAuthor.addReader(wangSanReader);
        // 张三订了平头哥的公众号
        pingtougeAuthor.addReader(zhangsanReader);
//
//        pingtougeAuthor.writeArticle("看完这篇你还不知道这些队列,我这些图白作了");

        // 李四订了平头哥的公众号
        pingtougeAuthor.addReader(liSiReader);

//        pingtougeAuthor.writeArticle("实现 Java 本地缓存,该从这几点开始");
        // 张三取关了平头哥的公众号
        pingtougeAuthor.deleteReader(zhangsanReader);
        pingtougeAuthor.writeArticle("实观察者模式,从微信公众号说起");

    }
}

测试结果:

2019-09-18 20-21-20屏幕截图.png

张三取关之后,平头哥发布的文章,他将不会再接收到推送通知。在上面公众号群发的模拟场景中,我们使用到了一种设计模式,叫做观察者模式,那今天我们就一起来简单的了解一下观察者模式。

观察者模式定义

观察者模式又叫发布-订阅模式,发布-订阅模式大家应该非常熟悉了吧,消息中间件就是发布-订阅模式,观察者模式主要也是应用在消息中间件。观察者模式定义了一种一对多的依赖关系让多个订阅者同时监听某一个对象主题,这个主题对象在状态发生变化时,会通知所有的订阅者,让他们自己更新自己。这些特点在我们的模拟场景中基本上都体现出来了,如果你对这些有疑问,可以多看我们的模拟场景。

观察者的结构

  • 抽象主题(Subject)角色:抽取出了主题所需要定义的接口,比如我们的Author
  • 具体主题(Concrete Subject)角色:具体的主题实现者,该类必须继承抽象主题,比如我们的PingtougeAuthor
  • 抽象观察者(Observer)角色:观察者抽象类,定义观察者需要的接口,比如我们的Reader
  • 具体观察者(Concrete Observer)角色:具体的观察者,做这具体业务的类,比如我们的三个订阅者

观察者的 UML 图

3-1Q1161A6221S.gif

观察者模式的优点

  • 主题与观察者之间松耦合
  • 支撑广播通信:一条消息可以通知给多个人
  • 建立一条触发链:A触发B,B触发C,一条触发链,不过这个需要注意的地方很多

观察者的缺点

  • 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率
  • 如果采用顺序通知,当某个观察者卡住了,其他的观察者将无法接收到通知
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃

观察者模式差不多就这些内容,相对来说比较容易理解,另外多说一句,在JDK中已经内置了观察者模式,在java.util下的ObservableObserver两个类,有兴趣的可以去了解一下。

源代码

文章不足之处,望大家多多指点,共同学习,共同进步

原文发布于微信公众号 - 平头哥的技术博文(z694644032)

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Netflix是什么,与Spring Cloud有什么关系

    1、首先,Netflix是一家做视频的网站,可以这么说该网站上的美剧应该是最火的。

    Jackson0714
  • Django实战-天气接口封装

    Django网络应用开发的5项基础核心技术包括模型(Model)的设计,URL 的设计与配置,View(视图)的编写,Template(模板)的设计和Form(...

    小团子
  • Python全栈开发-有趣的小程序

    公众号---志学Python
  • 搜索的B面:新连接

    在一些悲观者认为“我用搜索越来越少”时,8月,百度App宣布日活突破2亿,成为为数不多的进入日活2亿俱乐部的成员,用数据回应了“搜索是否有人用”的问题,百度Ap...

    罗超频道
  • 【H5】344- 微信 H5 页面兼容性解决方案

    当点击输入的时候,光标的高度和父盒子的高度一样。例如下图,左图是正常所期待的输入框光标,右边是ios的input光标。

    pingan8787
  • react页面内嵌微信二维码 和 自定义样式 以及 微信网页共用unionId问题

      在react页面内嵌“微信二维码”,实现PC端通过微信扫码进行登录。首先去微信开放平台注册一个账号,创建一个网站应用,提交网站备案审核,获取appid和ap...

    tandaxia
  • WebRTC常见问题 (FAQ)

    业务后台实现,关于userSig参考:https://cloud.tencent.com/document/product/647/17275

    jialuhu
  • 巧用云开发,实现多个小程序访问同一个云数据库

    经常看我文章的知道,我有两个博客小程序(程序员的博客和我si程序员)。前者基于开源博客框架ghost。

    Bug生活2048
  • 校园技术工坊丨云开发校园执行官招募开启!

    ? 云开发技术工作坊校园执行官报名正式开启!寻找优秀的你,与云开发一同奔赴一场关于青春和热爱的约会! ? 转眼间,2019暑假余额已用完~ 又来到了元气满满的...

    腾讯技术工程官方号
  • 优美库图片小程序 Version1.0

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

    xbhog

扫码关注云+社区

领取腾讯云代金券