【设计模式】汉堡中的设计模式——观察者模式

【设计模式】汉堡中的设计模式——观察者模式

情景带入

对于爱吃麦当劳的我来说,自然是不想错过各种动态,可是我又不可能【无时无刻】的蹲在店里等新品吧(还是要搬砖的)
那么有没有一种好的方法,在麦当劳【推出新品或发布动态】的时候,我能及时收到通知?
或许有人会说,你把麦当劳内部员工要一下微信不久可以了,这样一有新品,就让他来通知一下你就好啦!
emm理想确实是很丰满的,但是现实却是老子哪里会搭讪….

那难道就没有办法了吗?

怎么可能,你关注下公众号不久可以了???

【设计模式】汉堡中的设计模式——观察者模式

为什么关注公众号就可以

我们都知道, 关注指定公众号之后,只要该公众号发布动态的时候,你就可以在【订阅号消息】中看到新动态,正如下图所示

【设计模式】汉堡中的设计模式——观察者模式

公众号是基于 发布-订阅模式,了解这个跟我们今天要讲的 观察者模式有什么关联吗?

发布者-订阅者模式与观察者模式

观察者模式(Observer)

虽然观察者模式也被称为”发布-订阅”模式,但是我认为 这个发布-订阅跟接下来要讲的发布者-订阅者模式是不同

观察者模式的定义是:当对象存在一对多关系的时候,一个对象修改了自身,则会自动通知依赖他的对象

带入我们的例子中,就是有很多个吃货(Observer)”观察着” 麦当劳公众号(Subject)

Observer、Subject是观察者模式的抽象描述,而他的类图类似是如此的,其中Observer是个抽象类或者接口,底下就是真正的观察者,Subject在这里没有设计成抽象的,你也可以让Subject抽象起来,再给出具体的实现类

【设计模式】汉堡中的设计模式——观察者模式

现在对照着类图,再看一下 观察者模式的定义,多个观察者把自己注册到Subject中,当Subject发生变动,便会触发通知方法,而通知方法就会调用各个具体观察者里面的具体实现

这便是观察者模式基本的概念,那么观察者模式的好处究竟是什么?

  1. 我们可以知道,Subject(被观察者)和Observer(观察者)是抽象耦合的,也即你观察者怎么变化都不会影响到Subject,如果要新增观察者,也只需要新建一个类,然后注册到Subject里面即可
  2. 拥有触发机制,一个对象更改,关联对象就可以收到通知,甚是方便

那么观察者的缺点又是什么?

  1. 细心观察定义,Subject里面可能会有多个观察者,那么这个多个,究竟可能会有多少个?不确定,如果有3亿的人关注了麦当劳公众号,那么发布动态的时候,就会通知到3亿人, 会耗费很多时间
  2. 这种真的就是”被通知”,也即观察者不知道中间经历了什么,而只是知道被观察的对象发生了变化( 类似一个女生突然找你吃饭,你还觉得很高兴,殊不知也许她刚刚被放飞机了,所以才找你 ,你不知道个中缘由,当然这也未必是坏事:),因为这样你就可以确定,我不需要知道你怎么做,而只需要这么做,我便能被通知到,观看的角度不一样,就会有不同的想法 )

其实发布-订阅模式跟观察者模式之间最大的区别就是 Publisher和Subscriber间,还存在着一个中间件来负责调度,发布者不需要知道订阅者是谁,反过来也是一样的

这么一说就有点像Kafka里面的producer-topic-consumer了,简单画个图

【设计模式】汉堡中的设计模式——观察者模式

当然实际上,消费者从属于消费者群组,一个群组里面的消费者订阅的是同一个主题,每个消费者接收主题一部分分区的消息,也即对应里面具体的partition,这里就不额外展开了

正如上图,producer和consumer是不认识的,但是他们都认识一个家伙,那就是topic,producer充当publisher的角色,把信息发送到topic,消费者监听(订阅)topic,一旦这个topic接收到信息,就会把信息推送给监听的消费者

观察者模式主要是以同步的方式触发机制,而发布-订阅更多的是用在异步的情况下,借助类似Kafka的消息中间件完成组件间的松散耦合

所以其他两者并不等价,只能说有那么几分相似

观察者模式的落地实现

  1. 首先按照想法,我们得有一个McDonalds的Subject,假设这就是【麦当劳官方公众号】,里面有动态发布的方法、注册/移除观察者的方法、以及一键消息提醒的方法
/**
 * @Author: Amg
 * @Date: Created in 23:04 2021/11/15
 * @Description: 麦当劳公众号
 */
public class McDonalds {

    //观察者的集合,需要这里这里泛型接收的是观察者的顶层类
    private List list = new ArrayList<>();
    //接收新动态
    public String status;

    public McDonalds(String status) {
        this.status = status;
    }

    /**
     * 添加观察者
     * @param observer
     */
    public void registerObserver(Observer observer) {
        list.add(observer);
    }

    /**
     * 移除观察者
     * @param observer
     */
    public void removeObserver(Observer observer) {
        list.remove(observer);
    }

    /**
     * 遍历集合,取出每一个观察者,然后调用他们的update方法
     */
    public void notifyAllObserver() {
        System.out.println("[麦当劳]:" + status);
        for (Observer observer : list) {
            observer.update();
        }
    }

}

  1. 然后还得有观察者对象,观察者可能会有多个,所以我们直接建一个顶层抽象类,然后给出一个【吃货对象】
/**
 * @Author: Amg
 * @Date: Created in 23:04 2021/11/15
 * @Description: TODO
 */
public abstract class Observer {
    //名字
    protected String name;
    //话语
    protected String say;

    public Observer(String name,String say) {
        this.name = name;
        this.say = say;
    }

    abstract void update();
}

/**
 * @Author: Amg
 * @Date: Created in 23:10 2021/11/15
 * @Description: 吃货对象
 */
public class FoodieFan extends Observer {

    public FoodieFan(String name, String say) {
        super(name,say);
    }

    @Override
    void update() {
        System.out.println(String.format("[%s: %s]",name,say));
    }
}
  1. 当然,最好需要有一个客户端给展示起来
/**
 * @Author: Amg
 * @Date: Created in 23:08 2021/11/15
 * @Description: 客户端
 */
public class Client {

    public static void main(String[] args) {

        McDonalds mcDonalds = new McDonalds("[麦当劳新品推荐:敷面膜的安格斯,当前仅需41.5,赶快来品尝吧!]");

        FoodieFan foodieA = new FoodieFan("吃货Amg","这个汉堡可不能错过!");
        FoodieFan foodieB = new FoodieFan("吃货胖头鱼","emm这个我倒是没什么兴趣,有新品雪糕再通知我好了");
        FoodieFan foodieC = new FoodieFan("吃货大头虾","看来今晚的宵夜有着落了,这就去盘他");

        mcDonalds.registerObserver(foodieA);
        mcDonalds.registerObserver(foodieB);
        mcDonalds.registerObserver(foodieC);

        mcDonalds.notifyAllObserver();
    }
}

//最终输出的结果

[麦当劳]:[麦当劳新品推荐:敷面膜的安格斯,当前仅需41.5,赶快来品尝吧!]
[吃货Amg: 这个汉堡可不能错过!]
[吃货胖头鱼: emm这个我倒是没什么兴趣,有新品雪糕再通知我好了]
[吃货大头虾: 看来今晚的宵夜有着落了,这就去盘他]

只要思想不滑坡,代码还不是手到擒来,所以理解思想才是最关键的

总结

再总结一下 观察者面包与发布者-订阅者模式之间的区别

观察者模式 发布-订阅模式 Subject和Observer之间是直接沟通的 Publisher和Subscriber是不直接沟通,通过中间件来交流 观察者模式适用于单体应用程序 发布-订阅模式适用于跨应用的程序 观察者主要以同步方式实现 发布-订阅则主要以异步的方式实现

最后来一波王婆卖瓜,更多精彩尽在微信公众号【码农Amg】,这里将不定期的更新日常工作总结和学习中的重难点知识分享,赶快来订阅我吧!

【设计模式】汉堡中的设计模式——观察者模式

Original: https://www.cnblogs.com/iamamg97/p/15559252.html
Author: 码农Amg
Title: 【设计模式】汉堡中的设计模式——观察者模式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/570331/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Java并发编程艺术系列-三、Java内存模型

    三、Java内存模型 本章大致分四个部分: Java内存模型的基础:主要介绍内存模型相关的基本概念; Java内存模型中的顺序一致性:主要介绍重排序与顺序一致性内存模型; 同步原语…

    Java 2023年6月9日
    071
  • 8、BigInteger和BigDecimal类

    BigDecimal适合保存进度更高的浮点型(小数) 常见方法 add 加 subtract 减 multiply 乘 divide 除 //当我们编程中,需要处理很大的整数,lo…

    Java 2023年6月7日
    075
  • Spring学习笔记1

    前言:看了B站黑马程序员,狂神说JAVA,个人觉得还是狂神讲的更加细致,现记录自己的学习过程 一、Spring是一个框架,是为了让现有的技术更加容易使用,本身就是一个大杂烩,整合了…

    Java 2023年6月7日
    063
  • tomcat拦截特殊字符报400,如 “|” “{” “}” “,”等符号的解决方案

    最近在做一个项目,需要对外暴露两个接口接收别人给的参数,但是有一个问题就是对方的项目是一个老项目,在传参数的时候是将多个字符放在一个参数里面用”|”进行分割…

    Java 2023年6月13日
    075
  • springboot2 xxl-job 调度中心

    1,下载自己需要的版本 1,https://github.com/xuxueli/xxl-job2,https://gitee.com/xuxueli0323/xxl-job/re…

    Java 2023年5月30日
    073
  • 深入理解this关键字

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月7日
    070
  • Dubbo系列之服务暴露过程

    这周去苏州见大佬,没想到遇到一堆女粉丝,其中居然还有澡堂子堂妹,堂妹一遇到我就说敖丙哥哥我超级喜欢你写的dubbo系列,你能跟我好好讲一下他的服务暴露过程么? 我笑了笑:傻瓜,你想…

    Java 2023年6月9日
    072
  • springcloud2.0以上版本_eureka控制台显示_找不到${spring.cloud.client.ipAddress}_没有显示成IP地址

    一般会是 eureka.instance.prefer-ip-address=trueeureka.instance.instance-id={$spring.cloud.clie…

    Java 2023年5月30日
    059
  • 【特殊的阻塞队列】 java.util.concurrent.SynchronousQueue 源码分析

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月9日
    063
  • 23.线程锁的使用

    不合理的设定临界区域,会让线程的调用失去意义。 1.不应该频繁的使用锁 2.减小锁使用的区域,线程公共资源之外 的资源 尽量不要放到临界区。 示例二:(不用线程) 示例三:(使用线…

    Java 2023年5月29日
    0116
  • leetcode 105. Construct Binary Tree from Preorder and Inorder Traversal 从前序与中序遍历序列构造二叉树(中等)

    一、题目大意 给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节…

    Java 2023年6月13日
    081
  • 关于工资倒挂

    工资倒挂是指「新员工能力不如老员工,工资却高过老员工」。 如果你是上述老员工 你会觉得不爽,因为不公平。但在多数情况下,其实这件事并不坏: 你打算离职 从新员工工资来看, 你的市场…

    Java 2023年6月16日
    098
  • 4.如何避免缓存穿透、缓存击穿、缓存雪崩

    先来看一下缓存穿透,是指业务请求穿过了缓存层,落到持久化存储上。在大多数场景下,我们应用缓存是为了承载前端业务请求,缓存被击穿以后,如果请求量比较大,则会导致数据库出现风险。 以双…

    Java 2023年6月9日
    044
  • 【Java分享客栈】未来迈向高级工程师绕不过的技能:JMeter压测

    前言 因为工作需要,久违的从自己的有道云笔记中去寻找压测相关的内容,翻开之后发现还不错,温故一遍后顺便整理出来分享给大家。 题外话,工作8年多,有道云笔记不知不觉都6G多了,扫一眼…

    Java 2023年6月9日
    088
  • 萌新也能看懂的KMP算法

    前言 算法是什么?算法就是数学规律.怎么去总结和发现这个规律,就是理解算法的过程. KMP算法的本质是穷举法,而并不是去创造新的匹配逻辑. 以下将搜寻的字符串称为子串(part),…

    Java 2023年6月8日
    087
  • Springboot+Websocket+JWT实现的即时通讯模块

    场景 目前做了一个接口:邀请用户成为某课程的管理员,于是我感觉有能在用户被邀请之后能有个立马通知他本人的机(类似微博、朋友圈被点赞后就有立马能收到通知一样),于是就琢磨琢磨搞了一套…

    Java 2023年6月7日
    083
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球