Guava、Spring 如何抽象观察者模式?

架构 2023-07-05 17:29:38
184阅读

文中转载微信公众平台「源代码兴趣爱好圈」,创作者龙台。转截文中请联络源代码兴趣爱好圈微信公众号。 

今日解读一篇个人行为型策略模式,什么叫个人行为型?个人行为型关键承担设计方案 类或目标中间的互动。工作上常见的观察者模式便是一种个人行为型策略模式

近期在试着重新构建以前读过的编码。在再次整理过业务流程以后,发觉现有的设计方案情景应当可以连接到策略模式,并且查询了编码的递交纪录,也是坚定不移了此念头

维持以前的一贯作风,要想表明一个策略模式,必须三板斧支撑点。什么叫观察者模式?怎么使用观察者模式?新项目中应当怎样运用?

观测者策略模式考试大纲以下:

  1. 什么叫观察者模式
  2. 观察者模式编码要怎么写
  3. 怎么使用观察者模式融合业务流程
  4. Guava EventBus 观察者模式
  5. Spring ApplicationEvent 事情实体模型
  6. 观察者模式最终的小结

什么叫观察者模式

观察者模式 是一种个人行为策略模式,容许界定一种定阅通告体制,能够在目标(被观测者)事情产生时通告好几个 “观查” 该目标的观测者目标,因此也被称作 公布定阅方式

其实我本人来讲,不太喜爱应用文本去界定一种策略模式的词义,由于那样一直难以理解。因此就拥有下边日常生活的事例,来协助阅读者更强的去了解方式的词义。类图以下所显示:

在举例子前,先使我们了解下观察者模式中的 人物角色种类 及其编码实例。观察者模式由下列几一部分人物角色构成,能够参照编码实例去了解,不必被文字说明带偏

  • 主题风格(被观测者)(Subject):抽象性主题风格人物角色把全部观测者目标储存在一个器皿里,出示加上和清除观测者插口,而且出示出通告全部观测者目标插口(也是有创作者根据 Observable 叙述)
  • 实际主题风格(实际被观测者)(Concrete Subject):实际主题风格人物角色的岗位职责便是完成抽象性总体目标人物角色的插口词义,在被观测者情况变更时,给器皿内全部申请注册观测者推送情况通告
 
  1. public interface Subject { 
  2.     void register(Observer observer);  // 加上观测者 
  3.     void remove(Observer observer);  // 清除观测者 
  4.     void notify(String message);  // 通告全部观测者事情 
  5.  
  6. public class ConcreteSubject implements Subject { 
  7.     private static final List<Observer> observers = new ArrayList(); 
  8.  
  9.     @Override 
  10.     public void register(Observer observer) { observers.add(observer); } 
  11.  
  12.     @Override 
  13.     public void remove(Observer observer) { observers.remove(observer); } 
  14.  
  15.     @Override 
  16.     public void notify(String message) { observers.forEach(each -> each.update(message)); } 
  • 抽象性观测者(Observer):抽象性观测者人物角色是观测者的个人行为抽象性,它界定了一个改动插口,当被观测者传出事情时通告自身
  • 实际观测者(Concrete Observer):完成抽象性观测者界定的升级插口,能够在被观测者传出事情时通告自身
 
  1. public interface Observer { 
  2.     void update(String message);  // String 入参仅仅举例说明, 真正业务流程不容易限定 
  3.  
  4. public class ConcreteObserverOne implements Observer { 
  5.     @Override 
  6.     public void update(String message) { 
  7.         // 实行 message 逻辑性 
  8.         System.out.println("接受到被观测者情况变动-1"); 
  9.     } 
  10.  
  11. public class ConcreteObserverTwo implements Observer { 
  12.     @Override 
  13.     public void update(String message) { 
  14.         // 实行 message 逻辑性 
  15.         System.out.println("接受到被观测者情况变动-2"); 
  16.     } 

大家跑一下上边的观察者模式实例,假如可以的话得话会将2个观测者实行逻辑性中的日志打印。如果是平时领域模型,抽象性观测者界定的入参是具备业务流程实际意义的,大伙儿能够对比新项目上应用到的 MQ Message 体制

 
  1. public class Example { 
  2.     public static void main(String[] args) { 
  3.         ConcreteSubject subject = new ConcreteSubject(); 
  4.         subject.register(new ConcreteObserverOne()); 
  5.         subject.register(new ConcreteObserverTwo()); 
  6.         subject.notify("被观测者情况更改, 通告全部已申请注册观测者"); 
  7.     } 

观察者模式融合业务流程

由于企业业务场景保密性,因此下边大家根据【新警察故事】的影片剧情,略微伪造下故事情节,模拟大家的观察者模式应用领域

假定:现阶段大家有三个警员,分别是龙哥、锋哥、老三,她们授命跟踪嫌疑人阿祖。假如发觉嫌疑人阿祖有声响,龙哥、峰哥承担执行追捕行動,老三向公安局摇人,流程表以下:

假如说应用基本编码写这套步骤,是可以实现需求的,一把梭的逻辑性能够完成一切要求。可是,假如说下一次行動,龙哥让老三跟随自身执行追捕,亦换句话说龙哥精英团队扩大,来啦老四、老五、老六...

比照观察者模式人物角色界定,老四、老五、老六全是实际的观测者(Concrete Observer)

假如依照上边的构想,大家根据“一把梭”的方法把编码写出去会有哪些难题呢?以下:

  1. 第一个,提升了编码的多元性。完成类换句话说这一方式涵数奇大极其,由于伴随着警务人员的扩大,代码块会越来越大
  2. 违反了开闭原则,由于会经常修改不一样警务人员的每日任务。每一个警务人员的每日任务并不是一成不变的,举个事例而言此次对于案犯,让峰哥执行的追捕行動,下一次就可能是消防疏散群众,难道说每一次的变更都必须修改“一把梭”的编码

第一种我们可以根据,大涵数拆小涵数 或是 类别拆分成小项 的方法处理编码承担性的问题。可是,开闭原则却不可以防止掉,由于伴随着警务人员(观测者)的增加及降低,必定会遭遇经常修改原函数的状况

在我们应对这类 已经知道会变化,而且很有可能会 经常变化不固定不动 的编码,就需要应用抽象思维能力来开展设计方案,从而维持编码的简约、可维护保养

这儿应用 Java SpringBoot 新项目构造来撰写观察者模式,编码最后消息推送到 Github 库房。阅读者能够先把库房拉出来,由于在其中不仅实例编码,还包含 Guava 和 Spring 的观察者模式完成 GitHub 库房详细地址

最先,界定观察者模式中的观测者人物角色,各自为抽象性观测者插口及其三个实际观测者完成类。具体业务流程中,策略模式会和 Spring 架构紧密结合,因此实例编码中包括 Spring 有关注释及插口

次之,界定抽象性被观测者插口及其实际被观测者完成类。跟上面一样,被观测者也必须变成 Spring Bean,代管于 IOC 器皿管理方法

到这儿,一个详细的观察者模式就完成了。可是,仔细的阅读者会发觉那样的观察者模式会有一个小问题,这儿先不表明,再次往下看。下面就必须具体练习一番,申请注册这种观测者,根据被观测者开启事情来通告观测者

怎样完成开闭原则

看过运用的编码以后,涵数体过大的难题早已被解决了,大家根据 分拆变成不一样的实际的观测者类 来分拆整体逻辑性。可是开闭原则难题呢?这就是上边所讲的存在的问题,大家现阶段是根据 表明的引进实际观察者模式 来开展加上到被观测者的通告器皿中,假如事后加上警员老四、老五... 愈来愈多的警员时,或是必须修改原来编码,难题应当怎么解决呢

实际上比较简单,平时 Web 新项目基本上都是会应用 Spring 架构开发设计,那当然是要应用在其中的特点处理情景难题。大家这儿根据 更新改造实际被观测者完成开闭原则

假如看了以前创作者读过的策略模式文章内容,对 InitializingBean 插口不容易觉得生疏,我们在 afterPropertiesSet 方式中,根据引入的 IOC 器皿获得到全部观测者目标 并加上至被观测者通告器皿中。那样的话,开启观测者事情,编码中只必须一行就可以进行通告

 
  1. @PostConstruct 
  2. public void executor() { 
  3.     // 被观测者开启事情, 通告全部观测者 
  4.     subject.notify("阿祖有行動!"); 

事后假如还有新的观测者类加上,只必须建立新的类完成抽象性观测者插口就可以进行要求。有时,可以被封裝起來的不止是 DateUtil 种类的java工具,一些策略模式还可以被封裝,进而更强的服务项目开发人员熟练掌握。这儿会各自详细介绍 Guava#EventBus 及其 Spring#事情实体模型

同步异步的定义

在详细介绍 EventBus 和 Spring 事情实体模型以前,有一道绕不以往的弯,那便是同歩实行、多线程实行的定义,及其在哪些的情景下应用同歩、多线程实体模型?

  • 同歩实行:说白了同歩实行,指的便是在传出一个要求后,在沒有得到启用結果以前,调用者便会等候在当今编码。直至获得到启用方式的实行結果,才算作完毕。小结一句话便是 由调用者积极等候这一启用的結果,未回到以前不实行其他实际操作
  • 多线程实行:而多线程实行正好相反,传出启用要求后马上回到,并往下实行编码。异步调用方式一般不容易有回到結果,启用以后就可以实行其他实际操作,一般通过回调函数的方法通告调用者結果

这儿给大伙儿举个事例,可以非常好的反映同歩、多线程的定义。例如你要想给体检医院通电话预定常规体检,你讲出自身要想预定的時间后,正对面的漂亮小姐姐说:“稍等片刻,我查一下時间是不是能够”,这个时候假如你 不挂电话,等待漂亮小姐姐查过对你说 以后才挂掉电話,那这就是同歩。假如她讲稍等片刻必须查一下,你告知她:“我先挂掉,查到結果后再打回来”,那这就是多线程 回调函数

在大家上边写的实例编码上,不容置疑是根据同歩的方式实行观察者模式,那是不是能够根据多线程的方法实行观测者个人行为?回答自然是能够。我们可以根据在 观察者模式个人行为实行前建立一个进程,那当然便是多线程的。自然,不太提议你那么做,那样很有可能会牵涉出大量的难题。一起来看下 Guava 和 Spring 是怎样封裝观察者模式

Guava EventBus 分析

EventBus 是 Google Guava 出示的信息公布-定阅类库,是策略模式中的观察者模式(生产制造/顾客实体模型)的經典完成

实际编码已提交 GitHub 代码仓库,EventBus 完成中包括同歩、多线程二种方法,代码库中由同歩方法完成观察者模式

由于 EventBus 并并不是文章内容关键,因此这儿总是对其基本原理开展讨论。最先 EventBus 是一个同歩类库,假如必须应用多线程的,那么就建立情况下特定 AsyncEventBus

 
  1. // 建立同歩 EventBus 
  2. EventBus eventBus = new EventBus(); 
  3.  
  4. // 建立多线程 AsyncEventBus 
  5. EventBus eventBus = new AsyncEventBus(Executors.newFixedThreadPool(10)); 

留意一点,建立 AsyncEventBus 必须特定线程池,其內部并沒有默认设置特定。自然也别像上边编码立即用 Executors 建立,作者是为了更好地图方便,假如从标准来讲,或是停止的应用默认设置线程池搭建方式建立 new ThreadPoolExecutor(xxx);

EventBus 同歩完成有一个较为有趣的点。观测者实际操作同歩、多线程个人行为时,均应用 Executor 去实行观测者內部编码,那怎样确保 Executor 能同歩实行呢。Guava 是那么做的:完成 Executor 插口,调用实行方式,启用 run 方式

 
  1. enum DirectExecutor implements Executor { 
  2.     INSTANCE; 
  3.  
  4.     @Override 
  5.     public void execute(Runnable command) { 
  6.         command.run(); 
  7.     } 

大伙儿有兴趣爱好能够去看看下 EventBus 源代码,并不是难以了解,工作中应用上或是挺便捷的。只不过是也是有不太好的地区,由于 EventBus 归属于过程内实际操作,假如应用多线程 AsyncEventBus 实行业务流程,存有遗失每日任务的很有可能

Spring 事情实体模型

Spring 大拿设计方案的观察者模式抽象性是创作者见到的最雅致、最作用的设计方案,假如要想玩乐观察者模式强烈推荐指数值 ??????????

假如要想应用 ApplicationEvent 轻松玩观察者模式,只必须简易两步。小结:实际操作简易,功能齐全

建立业务流程有关的 MyEvent,必须承继 ApplicationEvent,调用有参构造方法

界定不一样的窃听器(观测者)例如 ListenerOne 完成 ApplicationListener 插口,调用 onApplicationEvent 方式

根据 ApplicationContext#publishEvent 方式公布实际事情

Spring 事情与 Guava EventBus 一样,编码也不黏贴了,都早已储放到 Github 代码仓库。这儿关键详细介绍下 Spring 事情实体模型的特性,及其应用事宜

Spring 事情一样适用异步编程,必须在实际 Listener 完成类上加上 @Async 注释。适用 Listener 定阅的次序,例如有 A、B、C 三个 Listener。能够根据 @Order 注释完成好几个观测者次序消費

创作者提议阅读者盆友一定要跑下 ApplicationEvent 的 Demo,在应用架构的另外也 要有效的应用架构出示的专用工具车轮子,由于被架构封裝出的作用,一般而言要比自身写的作用更强劲、发生难题的概率更少。另外,谨记不必造反复车轮子,除非是作用点不符合的状况下,能够参考原来车轮子的基本上开发设计自身作用

结言

文章内容根据图片配文字的方法协助大伙儿整理了下观察者模式的完成方法,也是发布了升阶版的 EventBus 及其 ApplicationEvent,坚信大伙儿看了以后能够很开心的在自身新项目中玩乐策略模式了。谨记哈,要在有效的情景下应用方式,一般而言观察者模式功效于 观测者与被观测者中间的解藕合

最终解释下最开始提及的难题,新项目中的观察者模式 应当应用同歩实体模型或是多线程实体模型呢

假如仅仅应用观察者模式分拆编码使其达到 开闭原则、高内聚力低耦合、岗位职责单一 等特点,那麼当然是应用同歩去做,由于这类方法是更为妥当。而假如 不关注观测者实行結果或是考虑到特性 等状况,则能够应用多线程的方法,根据回调函数的方法达到业务流程回到要求

有关观测者策略模式文中就讲到这儿,后边会相继輸出加工厂、原形、享元等方式;假如文章内容对您有协助那么就点个关心适用一下吧,祝好。

the end
免责声明:本文不代表本站的观点和立场,如有侵权请联系本站删除!本站仅提供信息存储空间服务。