阿里P6+面试:介绍下观察者模式?
有情怀,有干货,微信搜索【三太子敖丙】关注这个有一点点东西的程序员。
本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点、资料以及我的系列文章。
消息队列(MQ),一种能实现生产者到消费者单向通信的通信模型,这也是现在常用的主流中间件。常见有 RabbitMQ、ActiveMQ、Kafka等 他们的特点也有很多 比如 解偶、异步、广播、削峰 等等多种优势特点。
在设计模式中也有一种模式能有效的达到解偶、异步的特点,那就是观察者模式又称为发布订阅模式。
今天阿丙就分享一下实际开发中比较常见的这种模式
大纲
定义
什么是观察者模式?他的目的是什么?
当一个对象的状态发生改变时,已经登记的其他对象能够观察到这一改变从而作出自己相对应的改变。通过这种方式来达到减少依赖关系,解耦合的作用。
举一个例子,就好比微信朋友圈,以当前个人作为订阅者,好友作为主题。一个人发一条动态朋友圈出去,他的好友都能看到这个朋友圈,并且可以在自主选择点赞或者评论。
感觉有点抽象,还是看看他有哪些主要角色:
- Subject(主题): 主要由类实现的可观察的接口,通知观察者使用attach方法,以及取消观察的detach方法。
- ConcreteSubject(具体主题): 是一个实现主题接口的类,处理观察者的变化
- Observe(观察者): 观察者是一个由对象水岸的接口,根据主题中的更改而进行更新。
这么看角色也不多,但是感觉还是有点抽象,我们还是用具体实例代码来走一遍吧,我们还是以上面的朋友圈为例看看代码实现
public interface Subject {// 添加订阅关系void attach(Observer observer);// 移除订阅关系void detach(Observer observer);// 通知订阅者void notifyObservers(String message);
}
先创建一个主题定义,定义添加删除关系以及通知订阅者
public class ConcreteSubject implements Subject {// 订阅者容器private List<Observer> observers = new ArrayList<Observer>();@Overridepublic void attach(Observer observer) {// 添加订阅关系observers.add(observer);}@Overridepublic void detach(Observer observer) {// 移除订阅关系observers.remove(observer);}@Overridepublic void notifyObservers(String message) {// 通知订阅者们for (Observer observer : observers) {observer.update(message);}}
}
其次再创建的具体主题,并且构建一个容器来维护订阅关系,支持添加删除关系,以及通知订阅者
public interface Observer {// 处理业务逻辑void update(String message);
}
创建一个观察者接口,方便我们管理
public class FriendOneObserver implements Observer {@Overridepublic void update(String message) {// 模拟处理业务逻辑System.out.println("FriendOne 知道了你发动态了" + message);}
}
最后就是创建具体的观察者类,实现观察者接口的update方法,处理本身的业务逻辑
public class test {public static void main(String[] args) {ConcreteSubject subject = new ConcreteSubject();// 这里假设是添加好友subject.attach(new FriendOneObserver());FriendTwoObserver twoObserver = new FriendTwoObserver();subject.attach(twoObserver);// 发送朋友圈动态subject.notifyObservers("第一个朋友圈消息");// 输出结果: FriendOne 知道了你发动态了第一个朋友圈消息// FriendTwo 知道了你发动态了第一个朋友圈消息// 这里发现 twoObserver 是个推荐卖茶叶的,删除好友subject.detach(twoObserver);subject.notifyObservers("第二个朋友圈消息");// 输出结果:FriendOne 知道了你发动态了第二个朋友圈消息}
}
最后就是看测试结果了,通过ConcreteSubject 维护了一个订阅关系,在通过notifyObservers 方法通知订阅者之后,观察者都获取到消息从而处理自己的业务逻辑。
这里细心的朋友已经达到了解耦合的效果,同时也减少了依赖关系,每个观察者根本不要知道发布者处理了什么业务逻辑,也不用依赖发布者任何业务模型,只关心自己本身需要处理的逻辑就可以了。
如果有新的业务添加进来,我们也只需要创建一个新的订阅者,并且维护到observers 容器中即可,也符合我们的开闭原则。
这里只是一种同步的实现方式,我们还可以扩展更多其他的异步实现方式,或者采用多线程等实现方式。
框架应用
观察者模式在框架的中的应用也是应该很多
第一种 熟悉JDK的人应该知道 在java.util 包下 除了常用的 集合 和map之外还有一个Observable类,他的实现方式其实就是观察者模式。里面也有添加、删除、通知等方法。
这里需要注意是的 他是用Vector 作为订阅关系的容器,同时在他的定义方法中都添加synchronized关键字修饰类,以达到线程安全的目的
这里我贴出了关键源码,感兴趣的同学可以自己打开并且观看每个方法的注释。
第二种 在Spring中有一个ApplicationListener,也是采用观察者模式来处理的,ApplicationEventMulticaster作为主题,里面有添加,删除,通知等。
spring有一些内置的事件,当完成某种操作时会发出某些事件动作,他的处理方式也就上面的这种模式,当然这里面还有很多,我没有细讲,有兴趣的同学可以仔细了解下Spring的启动过程。
import java.util.EventListener;/*** Interface to be implemented by application event listeners.* Based on the standard {@code java.util.EventListener} interface* for the Observer design pattern. // 这里也已经说明是采用观察者模式** <p>As of Spring 3.0, an ApplicationListener can generically declare the event type* that it is interested in. When registered with a Spring ApplicationContext, events* will be filtered accordingly, with the listener getting invoked for matching event* objects only.** @author Rod Johnson* @author Juergen Hoeller* @param <E> the specific ApplicationEvent subclass to listen to* @see org.springframework.context.event.ApplicationEventMulticaster //主题*/
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {/*** Handle an application event.* @param event the event to respond to*/void onApplicationEvent(E event);}
第三种 Google Guava的事件处理机制Guava EventBus 他的实现也是采用设计模式中的观察者设计模式。
EventBus 当前实现有两种方式:
- EventBus // 同步阻塞模式
- AsyncEventBus // // 异步非阻塞模式
EventBus内部也提供来一系列的方法来供我们方便使用:
register 方法作为添加观察者
unregister方法删除观察者
post 方法发送通知消息等
使用起来非常方便。添加@Subscribe注解就可以创建一个订阅者了,具体的使用方式可以看看官网。
现实业务改造举例
框架应用的例子这么多,在业务场景中其实也有很多地方可以使用到,这里我还是给大家举一个例子。
在新用户注册成功之后我们需要给用户做两件事情,第一是发送注册成功短信,第二是给用发送新人优惠券。
看到这个问题 大家可能首先会想到用MQ消息处理呀,是的,用消息确实可以的,但是这里我们用观察者模式来实现这个问题,同时可以给大家演示一下,同步或者异步的问题。
public class SendNewPersonCouponObserver implements Observer {ExecutorService pool = Executors.newFixedThreadPool(2);@Overridepublic void update(String message) {Future<String> future = pool.submit(new Callable<String>() {@Overridepublic String call() throws Exception {TimeUnit.SECONDS.sleep(3);// 处理响应的业务逻辑return "调用发券服务,返回结果";}});try {// 假设等待200毫秒 没有获取到返回值结果则认为失败System.out.println(future.get(4000, TimeUnit.MILLISECONDS));} catch (Exception e) {// 执行异步获取失败// 记录日志,定时任务重试等}// 第一种不关心返回值结果Thread thread = new Thread(new Runnable() {@SneakyThrows@Overridepublic void run() {// 模拟服务调用 线程睡3秒钟TimeUnit.SECONDS.sleep(3);System.out.println("发送新人优惠券");}});thread.start();System.out.println("执行异步返回");}
}
public class SendSuccessMessageObserver implements Observer {@Overridepublic void update(String message) {// 处理业务逻辑System.out.println("注册成功");}public static void main(String[] args) {// 假设用户注册成功直接通知观察者,改干自己的事情了ConcreteSubject subject = buildSubject();subject.notifyObservers("");}private static ConcreteSubject buildSubject() {ConcreteSubject subject = new ConcreteSubject();subject.attach(new SendSuccessMessageObserver());subject.attach(new SendNewPersonCouponObserver());return subject;}
}
这里我们新写了两个观察者,主要看第一个SendNewPersonCouponObserver,这里了异步开启新的线程去处理我们的业务逻辑,当我们关心返回值的时候可以用Future来获取返回结果,当不关心的返回值的化,直接开启普通线程就可以了。
这个举例整体其实还是比较简单的主要是为了说清楚异步线程处理,当然如果用Guava EventBus也可以实现。而且也不复杂,感兴趣的朋友可以自己去试试。
当前现在有更加好的中间件MQ消息队列来处理这个业务问题,使得我们更加从容的面对这类场景问题,但是一些资源不足,不想引入新的系统。还是可以用这种方式来处理问题的。
设计模式学习的不是代码,而是学习每种模式的思想,他们分别处理的是什么业务场景。
总结
大家看完本篇文章不知道有发现没有,其实整个内容都是围绕了解耦的思想来写的,观察者模式作为行为型设计模式,主要也是为了不同的业务行为的代码解耦。
合理的使用设计模式可以使代码结构更加清晰,同时还能满足不同的小模块符合单一职责,以及开闭原则,从而达到前面写工厂模式说的,提高代码的可扩展性,维护成本低的特点。
我是敖丙你知道的越多,你不知道的越多,我们下期见。
敖丙把自己的面试文章整理成了一本电子书,共 1630页!
干货满满,字字精髓。目录如下,还有我复习时总结的面试题以及简历模板,现在免费送给大家。
链接:https://pan.baidu.com/s/1ZQEKJBgtYle3v-1LimcSwg 密码:wjk6
我是敖丙,你知道的越多,你不知道的越多,感谢各位人才的:点赞、收藏和评论,我们下期见!
文章持续更新,可以微信搜一搜「 三太子敖丙 」第一时间阅读,回复【资料】有我准备的一线大厂面试资料和简历模板,本文 GitHub https://github.com/JavaFamily 已经收录,有大厂面试完整考点,欢迎Star。
阿里P6+面试:介绍下观察者模式?相关推荐
- 阿里 P6 面试体验
点击上方"终端研发部",选择"星标" 回复"资源",领取全网最火的Java核心知识总结~ 本文转载自:code秘密花园"" ...
- 读者投稿:阿里 P6 面试体验
本文转载自:code秘密花园"" 17 年从上家公司离职之后就尝试过去阿里面试,结果还没进入状态就被凉凉了,在扇贝呆了 2 年多的时间,赶上正好租的房子要到期了,就想着再次尝试一下 ...
- 四面阿里P6+面试,提问率高达98%,会?基本offer稳了
余额宝一面 JVM 内存分哪几个区,每个区的作用是什么?JVM有哪些回收算法,对应的收集器有哪些? GC 的两种判定方法 ?CMS 收集器与 G1 收集器的特点. hashmap源码问题 HashMa ...
- ChatGPT教你面试阿里P6测试开发岗
持续坚持原创输出,点击蓝字关注我吧 最近ChatGPT爆火,ChatGPT能干什么呢?想必已经看过很多文章了,例如ChatGPT通过美国高考.ChatGPT开发游戏.调试代码.写文章等等. 哈哈,作 ...
- java定时器只执行一次_面试阿里P6,却被MySQL难倒,二战阿里,挤进天猫团队(Java岗)...
爱因斯坦说过"耐心和恒心总会得到报酬的",我也一直把这句话当做自己的座右铭,这句箴言在今年也彻底在我身上实现了. 每一个程序员都拥有一座大厂梦,我也不例外,去年面试阿里P6,竟然被 ...
- 刚参加完阿里Java P6面试归来,6点面试经验总结!(含必考题答案)
这是来自于优知学院一位铁粉面试回来的总结经验 刚参加完蚂蚁金服的Java P6级的面试,一共参加了4面.面试归来,总结下阿里面试流程.面试过程.以及面试题目范畴.文末有阿里Java P6面试必考题与答 ...
- 面试阿里P6,过关斩将直通2面,结果3面找了个架构师来吊打我
前言 人人都有大厂梦,对于程序员来说,BAT为首的一线互联网公司肯定是自己的心仪对象,毕竟能到这些大厂工作,不仅薪资高待遇好,而且能力技术都能够得到提升,最关键的是还能够给自己镀上一层金,让人瞻仰. ...
- 跳槽进阿里了,面试阿里P6也没那么难。
对于很多没有学历优势的人来说,面试大厂是非常困难的,这对我而言,也是一样,出身于二本,原本以为就三点一线的生活度过一生,直到生活上的变故,才让我有了新的想法和目标,因此我这个二本渣渣也奋斗了起来,竟拿 ...
- 2020年是意义非凡的一年,大专的我面试阿里P6居然过了
导语 学历永远是横在我们进人大厂的一道门槛,好像无论怎么努力,总能被那些985,211 按在地上摩擦! 不仅要被"他们"看不起,在HR挑选简历,学历这块就直接被刷下去了,连证明自己 ...
- 二本渣渣的我有幸通过简历,五轮面试,成功拿到阿里P6的offer!
学历真的是一个敲门砖,所以能升学历的小伙伴,一定要记得升学历!对于很多没有学历优势的人来说,面试大厂是非常困难的,很多时候连面试的机会都得不到.所以能得到面试机会,一定要好好把握,面试前准备一定要充分 ...
最新文章
- Android JNI开发入门之一
- Golang之单元测试
- BP神经网络预测实例
- Spring5源码 - 10 Spring事件监听机制_应用篇
- 将jar添加到发布目录_第32批免购置税新能源车型目录发布;通用BEV3平台将入华...
- php excel 分页,excel分页线怎么增加
- 森林病虫防治系统 (十)
- java testng 项目_java – Junit4和TestNG在Maven的一个项目中
- spring boot controller 初始化_使用 Spring 快速创建 web 应用的两种方式
- appscan无法连接到服务器_闪烁之光无法连接服务器怎么办 解决方案一览
- 蓝色的网站商城后台通用管理模板——后台
- 网页设计html颜色大全,50个使用柔和色彩的网页设计作品欣赏
- 浅谈 Spring 中的设计模式
- 解除隐藏文件cmd命令_cmd命令怎么隐藏电脑上的文件
- PMP项目进度网络图详解——第1篇:甘特图、PERT
- zoj3551 Bloodsucker//概率dp
- 女朋友生日java程序_★★女朋友要过生日了!我想用java为她写一个程序,一举两得啊! 希望大家多提建议啊!谢谢!!!...
- 使用appium进行app自动化测试时遇到AppActivity设置正确但报Connect Appium Server Fail.A new session could not be created
- LeetCode:数组(排列组合,二分查找I,二分查找II)
- 用c语言写鸡兔同笼问题
热门文章
- 区块链大本营专访 | MiiX全球技术开发者超级大赛,36小时,3支获胜队伍有哪些不一样的感受?
- 黑马程序员:Java学习路线图上线了
- Ramda.js 函数式编程 (==在angular中引入)
- 华为云IoT提出万物互联新范式,从万物感知到万物生长
- 台式计算机识别不了u盘启动,台式机不认U盘启动解决方法
- 线性代数之五:正交性
- java 定时为每月10号,Spring 定时任务如何实现每周一某个时间执行?
- 计算机木材染色 配色技术的应用技术,计算机配色技术在木材连缸染色中的应用研究.pdf...
- 解决warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
- Racecar 基于ROS通信机制的多点导航实验