设计模式之美
王争
前Google工程师,《数据结构与算法之美》专栏作者
立即订阅
21288 人已学习
课程目录
已更新 65 讲 / 共 100 讲
0/6登录后,你可以任选6讲全文学习。
开篇词 (1讲)
开篇词 | 一对一的设计与编码集训,让你告别没有成长的烂代码!
免费
设计模式学习导读 (3讲)
01 | 为什么说每个程序员都要尽早地学习并掌握设计模式相关知识?
02 | 从哪些维度评判代码质量的好坏?如何具备写出高质量代码的能力?
03 | 面向对象、设计原则、设计模式、编程规范、重构,这五者有何关系?
设计原则与思想:面向对象 (11讲)
04 | 理论一:当谈论面向对象的时候,我们到底在谈论什么?
05 | 理论二:封装、抽象、继承、多态分别可以解决哪些编程问题?
06 | 理论三:面向对象相比面向过程有哪些优势?面向过程真的过时了吗?
07 | 理论四:哪些代码设计看似是面向对象,实际是面向过程的?
08 | 理论五:接口vs抽象类的区别?如何用普通的类模拟抽象类和接口?
09 | 理论六:为什么基于接口而非实现编程?有必要为每个类都定义接口吗?
10 | 理论七:为何说要多用组合少用继承?如何决定该用组合还是继承?
11 | 实战一(上):业务开发常用的基于贫血模型的MVC架构违背OOP吗?
12 | 实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?
13 | 实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?
14 | 实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
设计原则与思想:设计原则 (12讲)
15 | 理论一:对于单一职责原则,如何判定某个类的职责是否够“单一”?
16 | 理论二:如何做到“对扩展开放、修改关闭”?扩展和修改各指什么?
17 | 理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?
18 | 理论四:接口隔离原则有哪三种应用?原则中的“接口”该如何理解?
19 | 理论五:控制反转、依赖反转、依赖注入,这三者有何区别和联系?
20 | 理论六:我为何说KISS、YAGNI原则看似简单,却经常被用错?
21 | 理论七:重复的代码就一定违背DRY吗?如何提高代码的复用性?
22 | 理论八:如何用迪米特法则(LOD)实现“高内聚、松耦合”?
23 | 实战一(上):针对业务系统的开发,如何做需求分析和设计?
24 | 实战一(下):如何实现一个遵从设计原则的积分兑换系统?
25 | 实战二(上):针对非业务的通用框架开发,如何做需求分析和设计?
26 | 实战二(下):如何实现一个支持各种统计规则的性能计数器?
设计原则与思想:规范与重构 (11讲)
27 | 理论一:什么情况下要重构?到底重构什么?又该如何重构?
28 | 理论二:为了保证重构不出错,有哪些非常能落地的技术手段?
29 | 理论三:什么是代码的可测试性?如何写出可测试性好的代码?
30 | 理论四:如何通过封装、抽象、模块化、中间层等解耦代码?
31 | 理论五:让你最快速地改善代码质量的20条编程规范(上)
32 | 理论五:让你最快速地改善代码质量的20条编程规范(中)
33 | 理论五:让你最快速地改善代码质量的20条编程规范(下)
34 | 实战一(上):通过一段ID生成器代码,学习如何发现代码质量问题
35 | 实战一(下):手把手带你将ID生成器代码从“能用”重构为“好用”
36 | 实战二(上):程序出错该返回啥?NULL、异常、错误码、空对象?
37 | 实战二(下):重构ID生成器项目中各函数的异常处理代码
设计原则与思想:总结课 (3讲)
38 | 总结回顾面向对象、设计原则、编程规范、重构技巧等知识点
39 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(上)
40 | 运用学过的设计原则和思想完善之前讲的性能计数器项目(下)
设计模式与范式:创建型 (7讲)
41 | 单例模式(上):为什么说支持懒加载的双重检测不比饿汉式更优?
42 | 单例模式(中):我为什么不推荐使用单例模式?又有何替代方案?
43 | 单例模式(下):如何设计实现一个集群环境下的分布式单例模式?
44 | 工厂模式(上):我为什么说没事不要随便用工厂模式创建对象?
45 | 工厂模式(下):如何设计实现一个Dependency Injection框架?
46 | 建造者模式:详解构造函数、set方法、建造者模式三种对象创建方式
47 | 原型模式:如何最快速地clone一个HashMap散列表?
设计模式与范式:结构型 (8讲)
48 | 代理模式:代理在RPC、缓存、监控等场景中的应用
49 | 桥接模式:如何实现支持不同类型和渠道的消息推送系统?
50 | 装饰器模式:通过剖析Java IO类库源码学习装饰器模式
51 | 适配器模式:代理、适配器、桥接、装饰,这四个模式有何区别?
52 | 门面模式:如何设计合理的接口粒度以兼顾接口的易用性和通用性?
53 | 组合模式:如何设计实现支持递归遍历的文件系统目录树结构?
54 | 享元模式(上):如何利用享元模式优化文本编辑器的内存占用?
55 | 享元模式(下):剖析享元模式在Java Integer、String中的应用
设计模式与范式:行为型 (6讲)
56 | 观察者模式(上):详解各种应用场景下观察者模式的不同实现方式
57 | 观察者模式(下):如何实现一个异步非阻塞的EventBus框架?
58 | 模板模式(上):剖析模板模式在JDK、Servlet、JUnit等中的应用
59 | 模板模式(下):模板模式与Callback回调函数有何区别和联系?
60 | 策略模式(上):如何避免冗长的if-else/switch分支判断代码?
61 | 策略模式(下):如何实现一个支持给不同大小文件排序的小程序?
不定期加餐 (3讲)
加餐一 | 用一篇文章带你了解专栏中用到的所有Java语法
加餐二 | 设计模式、重构、编程规范等相关书籍推荐
春节特别加餐 | 王争:如何学习《设计模式之美》专栏?
免费
设计模式之美
登录|注册

57 | 观察者模式(下):如何实现一个异步非阻塞的EventBus框架?

王争 2020-03-13
上一节课中,我们学习了观察者模式的原理、实现、应用场景,重点介绍了不同应用场景下,几种不同的实现方式,包括:同步阻塞、异步非阻塞、进程内、进程间的实现方式。
同步阻塞是最经典的实现方式,主要是为了代码解耦;异步非阻塞除了能实现代码解耦之外,还能提高代码的执行效率;进程间的观察者模式解耦更加彻底,一般是基于消息队列来实现,用来实现不同进程间的被观察者和观察者之间的交互。
今天,我们聚焦于异步非阻塞的观察者模式,带你实现一个类似 Google Guava EventBus 的通用框架。等你学完本节课之后,你会发现,实现一个框架也并非一件难事。
话不多说,让我们正式开始今天的学习吧!

异步非阻塞观察者模式的简易实现

上一节课中,我们讲到,对于异步非阻塞观察者模式,如果只是实现一个简易版本,不考虑任何通用性、复用性,实际上是非常容易的。
我们有两种实现方式。其中一种是:在每个 handleRegSuccess() 函数中创建一个新的线程执行代码逻辑;另一种是:在 UserController 的 register() 函数中使用线程池来执行每个观察者的 handleRegSuccess() 函数。两种实现方式的具体代码如下所示:
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《设计模式之美》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(33)

  • 小文同学
    Guava EventBus 对我来说简直是一份大礼。里面解耦功能使本来的旧项目又不可维护逐渐转化为可维护。

    EventBus作为一个总线,还考虑了递归传送事件的问题,可以选择广度优先传播和深度优先传播,遇到事件死循环的时候还会报错。Guava的项目对这个模块的封装非常值得我们去阅读,复杂的都在里头,外面极为易用,我拷贝了一份EventBus的代码进行修改以适配自己的项目,发觉里面的构造都极为精密巧妙,像一个机械钟表一样,自己都下不了手,觉得不小心就是弄坏了。

    跟随真正优秀的工程师,并阅读其写出来的代码让人受益匪浅。
    2020-03-13
    24
  • 下雨天
    课后题:
    代理模式,使用一个代理类专门来处理EventBus相关逻辑。作用:
    1.将业务与非业务逻辑分离
    2.后续替换EventBus实现方式直接改写代理类,满足拓展需求
    2020-03-13
    11
  • 辣么大
    重构使用代理模式,将非业务代码放到代理类中。
    另外试了争哥讲的EventBut类,在定义观察者的入参要修改成*Long*类型,如果使用long,这个方法是无法注册的,代码执行收不到通知。应该是ObserverRegistry类需要完善一下。
      @Subscribe
      public void handleRegSuccess(Long userId) {
        System.out.println("handleRegSuccess...");
        promotionService.issueNewUserExperienceCash(userId);
      }
    代码见:https://github.com/gdhucoder/Algorithms4/tree/master/designpattern/u57
    2020-03-13
    1
    5
  • 鱼Shiyao
    把老师的EventBus的代码实现了一下,发现有两个地方有问题。
    1:
    XMsg xMsg = new XMsg();
    YMsg yMsg = new YMsg();
    如果XMsg是YMsg的父类的话,应该是
    post(xMsg); => AObserver接收到消息
    post(yMsg); => AObserver,BObserver接收到消息
    2. 和刚才的问题一样,对应着ObserverRegistry的代码。
    在getMatchedObserverAction函数中
    if (postedEventType.isAssignableFrom(eventType))
    应该改成
    if (eventType.isAssignableFrom(postedEventType))
    2020-03-18
    4
    4
  • blacknhole
    提个问题:

    文中“所谓可匹配指的是,能接收的消息类型是发送消息(post 函数定义中的 event)类型的子类”这话似乎有问题,应该是父类吧?
    2020-03-15
    3
  • 九离
    什么开篇和语言没关系啊 好多文章全程到尾是java ,不是java的看这种文章根本看不了,只能看看理论篇和一些思想了。
    2020-03-20
    1
    2
  • 佐西玛
    争哥,在EventBus 框架功能需求介绍里面,如果XMsg 是 YMsg 的父类,则post(xMsg); => AObserver、BObserver接收到消息,这个地方应该是如果XMsg 是 YMsg 的子类。
    2020-03-14
    2
  • 老师,我们主要做物流方面的业务系统,类似仓储,港口这样的,流程繁杂。平时主要就是写增删改查,然后通过一个状态字段变化控制流程,所有业务代码流程中每一步操作都写满了各种状态验证,判断。后期稍微需求变动一点点,涉及到状态改动,要调整流程的话,都是一场灾难。针对我们这种系统,有办法将流程状态解耦出来吗?今天看到这篇事件总线的文章,好像看到希望,但是没想清具体怎么操作。不知道老师怎么看
    2020-03-13
    2
    2
  • Heaven
    对于这个问题,在UserCntroller中,我们应该只保留post函数() 发送的相关逻辑,而将注册Observer,初始化EventBus相关逻辑剔除,如果非要使用EventBus来实现的话,我们需要有人帮我们去进行注册和初始化,这时候就可以立马想到之前讲的工厂模式的DI框架,我们可以让所有观察者都被DI框架所管理,并且对EventBus创建一个装饰器类,在这个装饰器类中,由开发者选择注入线程池实现异步发送还是直接使用同步发送的,并且在init函数中 从DI框架管理的对象池中拿出所有标有@Subscribe注解的类,保存到ObserverRegistry中,对于所有需要使用EventBus的类,注入这个装饰器类即可,设计的好,甚至可以做到其他依赖代码都不用改一点
    2020-03-13
    2
  • Jeff.Smile
    在例子中当eventbus调用post传递的参数中是long userId,而两个observer被subcriber注解的方法参数都一样,此时这两个方法都会被调用到吗?
    2020-03-13
    2
  • Mogeko
    ObserverRegistry类的getMatchedObserverActions方法,postedEventType.isAssignableFrom(eventType)是不是反了?
    不是应该eventType.isAssignableFrom(postedEventType)吗?
    2020-03-18
    1
  • 右耳听海
    给老师点赞,虽然很早就接触了eventbus,但一直没明白这个的设计思想,现在有种醍醐灌顶的感觉
    2020-03-17
    1
  • Geek_76616d
    对Guava EventBus相见恨晚啊
    2020-03-16
    1
  • Frank
    为了让 UserController 更加聚焦在业务功能上,我的想法是将耦合的EventBus代码抽取出来形成一个单独的服务类,通过注入的方式注入到UserController类中使用。这样使其两者的职责单一,而新抽取出来的服务类可被其他业务场景复用。
    今天也加深了对Guava Eventbus的认识,虽然之前专栏也介绍过这个类库的使用。结合Jdk提供的java.util.Observable&Observer观察者模式API,与EventBus进行比对,如果要实现进程内的观察者使用EventBus最为方便。从JDK9之后,java.util.Observable&Observer已被标记为废弃,建议使用Java Beans规范中的事件模式和java.util.concurrent.Flow API。
    2020-03-14
    1
  • cricket1981
    public void handleRegSuccess(long userId) 方法签名中的long类型应该改成Long类型,不然SubscriberRegistry.getSubscribers(Object event)会匹配不上类型
    2020-03-14
    1
  • 爱麻将
    最近公司做了个业务系统架构重构,套用了其它公司的业务架构,架构与业务耦合的太紧,做起来非常痛苦,越来越觉得跟争哥写的专栏相违背。
    2020-03-14
    1
  • 小晏子
    我的想法比较直接,将UserController中的业务代码提出来放在接口的实现类中,这个UserController可以改名为EventController,然后这个接口实现类注入到这个EventController中,这样业务逻辑和控制逻辑就分离了,示例如下:
    interface iController {
        object register()
    }

    public class UserControllerService implement iController {
        private string telphone;
        private string password;

        public Long register() {
            long userId = userService.register(telephone, password);
            return userId;
      }
    }

    public class EventController {
        private iController iService;

        private EventBus eventBus;
        private static final int DEFAULT_EVENTBUS_THREAD_POOL_SIZE = 20;

        public EventController() {
            eventBus = new AsyncEventBus(Executors.newFixedThreadPool(DEFAULT_EVENTBUS_THREAD_POOL_SIZE)); // 异步非阻塞模式
        }

        public void setRegObservers(List<Object> observers) {
            for (Object observer : observers) {
                eventBus.register(observer);
            }
        }

        public void SendMessage() {
            object msg = iService.register()
            eventBus.post(msg)
        }

    }
    2020-03-13
    1
  • hanazawakana
    单独用一个工具类来处理eventbus相关的注册和post操作。然后通过依赖注入传给usercontroller
    2020-03-13
    1
  • 1012
    UserController 耦合的跟观察者模式相关的非业务代码可以使用代理模式进行重构
    2020-03-13
    1
  • Ken张云忠
    UserController 还是耦合了很多跟观察者模式相关的非业务代码,比如创建线程池、注册 Observer。为了让 UserController 更加聚焦在业务功能上,你有什么重构的建议吗?
    创建一个UserSubject类,将线程创建和注册Observer逻辑封装在进该类型,再通过依赖注入方式注入到UserController,最后UserController只需UserSubject的post函数就可以发送消息了.
    2020-03-13
    1
收起评论
33
返回
顶部