专栏电商日志财经减肥爱情
投稿投诉
爱情常识
搭配分娩
减肥两性
孕期塑形
财经教案
论文美文
日志体育
养生学堂
电商科学
头戴业界
专栏星座
用品音乐

一个由Java创始人ChrisWarth设计实现的设计模式

  一个由Java创始人ChrisWarth设计实现的设计模式;一个浪费了电商平台海量算力资源和网络资源的设计模式;一个为电商平台立下汗马功劳的设计模式;一个在JDK1。0就已经被全力支持,JDK9又被无情抛弃的设计模式;一个拥有多个化身的设计模式。
  大家好,极客架构师专注架构师成长,我是码农老吴。
  本期是《架构师基本功之设计模式》第16期。
  在第15期,我给大家分享了,行为型设计模式中的迭代器设计模式(Iteratordesignpattern),并对JDK框架集合中的ArrayList,HashMap中的迭代器进行了源码分析。
  本期,我将分享一个命运多舛,坚韧不拔的设计模式,它的发展历程源远流长:
  CS时代:发迹于GUI项目,
  BS时代:隐身于MVC模式,
  RPC时代:大器晚成,修成正果,成为了消息系统的灵魂。
  结论先行
  基于REIS分析模型,观察者模式由四种角色组成,分别是观察者角色(ObserverRole),被观察者角色(ObserableRole),消息角色(Messagerole),以及缓冲池角色(cachepoolrole),它的宗旨是,通过缓冲池角色,实现观察者角色与被观察者角色的解耦合。直播通知
  后面我分享的相关视频,一般会首先在直播中分享,希望大家多多捧场。
  本平台的直播时间,根据大家的投票结果,暂定为每周六晚上,具体时间待定。
  分享思路
  题外话:从埃隆马斯克(ElonMusk)吐槽RPC说起
  功臣还是罪人?
  案例介绍:订单流转
  第一版代码:无设计模式
  第二版代码:基于观察者模式
  观察者模式定义解析
  第三版代码:基于JDK内置的观察者模式模块
  REIS分析模式解析观察者模式
  观察者模式的化身
  通用代码和类图
  下期预告
  从埃隆马斯克(ElonMusk)吐槽RPC说起
  最近,美国硅谷的码农们正处于水深火热之中,Twitter的员工更是如履薄冰,战战兢兢。
  科技狂人埃隆马斯克,新官上任三把火,先是一口气裁员半数以上,接着发生马斯克吐槽twitterAPP滥用RPC技术,造成系统运行速度慢,被心直口快,口无遮拦的技术人员回怼(我估计是这几个码农的印度裔领导被开除,没人管造成的),老马发扬自己霸道总裁的作风,直接开除了怼他的员工的狗血剧情。
  从马斯克的嘴里,能说出RPC,说明他确实是技术出身,而且抓住了这个时代,软件开发的核心技术点。我认为,构成当今分布式微服务架构,至少有两块基石,那就是RPC和异步消息系统。RPC框架,我在后面的微服务架构系列专题中,会给大家分享。今天我们只聊异步消息系统背后的设计模式。
  谁敢怼我?
  功臣还是罪人?
  当你在电商平台,享受购物的乐趣时,是否想过,你的每个订单,电商平台的后台系统,会发生多少次运算(CPU算力资源)和网络传输(网络资源)呢?
  1次,10次,100次,1000次,还是10000次。
  在京东这样规模的电商平台,答案可能惊掉你的下巴。
  仅仅是订单信息的网络传输次数,就还需要再增加一个数量级,也就是以10万为单位来计算,而对订单进行业务校验的计算次数,则还需要根据购物车的商品数量,用网络传输次数再乘一个系数,数量之大超乎大家的想象。
  更离谱的是,这么多次的运算和网络传输,有多少是无效的呢?
  10,30,50,60,80,
  不对,都不对,还是太少,无效的数量往往在90以上,极端情况,无限地接近100。
  是不是危言耸听,哗众取宠呢,还是电商平台的架构师都是吃干饭的呢?
  一切的根源,都和我们今天要分享的设计模式,有很深的渊源。
  当用户在电商平台下单后,电商平台的订单中台,会给用户生成相应的订单,用户在自己的PC或者APP的订单管理模块,就可以看到自己的订单了。
  一个订单,一般包含多个SKU(也就是商品),当用户支付成功后,不同的SKU,要进行不同的后续处理。
  假如你购买的是手机号,运营商垂直业务部门的后台系统,就会先判断订单,是否是自己的订单,如果是,则根据用户的订单,在业务部门的系统中,生成相应的业务订单,并外调运营商(如中国移动或者中国联通)的BOSS系统,在运营商侧生成相应的订单。
  同样的,如果你购买的是机票,负责机票旅行的垂直业务部门,也会先判断是否是自己的订单,如果是,则在自己的业务系统,生成相应的业务订单,并外调相应的外部系统,产生机票订单。
  另外,电商平台各个业务部门,经常会举办成千上万的各种营销活动,每个活动都会涉及一部分SKU,这些SKU在用户下单并支付成功后,都需要进行一些额外的后续处理。
  而作为订单中台,是很难知道,一个订单都需要哪些业务系统进行后续处理的,那怎么办呢?
  这种业务场景,非消息系统莫属了。
  一方面,订单中台在用户支付成功后,发布订单支付成功的消息,而另一方面,垂直业务部门的业务系统,如果需要关注订单支付成功的消息,则在消息系统对这个消息进行注册。这样就意味着,有多少个业务系统注册了订单支付成功的消息,订单信息在消息系统中,就需要发送多少次。同时,无论前台生成多少订单,垂直业务部门的系统,都需要在接到全量订单的消息后,判断是否需要处理这个订单里面的商品,而大部分情况下,订单对具体的一个业务系统而言,都是无效的,这就是为什么,会造成海量的资源浪费的原因。
  对消息系统,有一定了解的朋友,可能都听说过,消息系统的背后,是生产者和消费者模式,还有发布和订阅模式,那么这和我们今天分享的观察者设计模式,有什么关系呢,听完后面的讲解,大家就会一清二楚。
  案例介绍:订单流转
  我们现在就针对上面的订单处理流程,开发相应的模块来实现。里面有两个模块,一个是订单中台的订单服务,用来支持用户下单,支付等操作。另一个是垂直业务模块,我们假设有两个垂直业务部门,手机业务部门和图书业务部门,对订单进行各自的后续处理。第一版代码:无设计模式类图
  接口及类:
  IOrderService:订单服务接口
  OrderService:订单服务实现类
  IBusinessService:垂直业务服务接口
  BookBusinessService:图书业务服务实现类
  MobileBusinessService:手机业务服务实现类
  OrderInfo:实体类,订单信息IOrderService:订单服务接口packagecom。geekarchitect。patterns。observer。demo01;author极客架构师吴念createTime20221117publicinterfaceIOrderService{voidpay(OrderInfoorderInfo);}OrderService:订单服务实现类
  这个类里面的pay方法是关键,当用户支付成功后,订单中台,调用各个垂直业务部门的服务,进行后续处理。packagecom。geekarchitect。patterns。observer。demo01;importorg。slf4j。Logger;importorg。slf4j。LoggerFactory;importorg。springframework。stereotype。Service;importjavax。annotation。Resource;author极客架构师吴念createTime20221117ServicepublicclassOrderServiceimplementsIOrderService{privatestaticfinalLoggerLOGLoggerFactory。getLogger(OrderService。class);Resource()privateIBusinessServicemobileBusinessService;Resource()privateIBusinessServicebookBusinessService;Overridepublicvoidpay(OrderInfoorderInfo){LOG。info(订单中台:订单支付成功);LOG。info(订单中台:逐个调用垂直业务部门的服务);mobileBusinessService。doService(orderInfo);bookBusinessService。doService(orderInfo);}}IBusinessService:垂直业务服务接口packagecom。geekarchitect。patterns。observer。demo01;author极客架构师吴念createTime20221117publicinterfaceIBusinessService{voiddoService(OrderInfoorderInfo);}BookBusinessService:图书业务服务实现类packagecom。geekarchitect。patterns。observer。demo01;importorg。slf4j。Logger;importorg。slf4j。LoggerFactory;importorg。springframework。stereotype。Component;手机业务服务author极客架构师吴念createTime20221117ComponentpublicclassBookBusinessServiceimplementsIBusinessService{privatestaticfinalLoggerLOGLoggerFactory。getLogger(BookBusinessService。class);OverridepublicvoiddoService(OrderInfoorderInfo){LOG。info(垂直业务部门:图书业务服务);}}OrderInfo:实体类,订单信息packagecom。geekarchitect。patterns。observer。demo01;importlombok。Data;author极客架构师吴念createTime20221117DatapublicclassOrderInfo{privateLongID;privateLongMemberID;privateintorderType;}测试类packagecom。geekarchitect。patterns。observer。demo01;importorg。junit。jupiter。api。AfterEach;importorg。junit。jupiter。api。BeforeEach;importorg。junit。jupiter。api。Test;importorg。slf4j。Logger;importorg。slf4j。LoggerFactory;importorg。springframework。beans。factory。annotation。Autowired;importorg。springframework。boot。test。context。SpringBootTest;importstaticorg。junit。jupiter。api。Assertions。;SpringBootTestclassOrderServiceTest{staticfinalLoggerLOGLoggerFactory。getLogger(OrderServiceTest。class);AutowiredprivateIOrderServiceorderService;BeforeEachvoidsetUp(){}AfterEachvoidtearDown(){}Testvoidpay(){OrderInfoorderInfonewOrderInfo();orderInfo。setID(1L);orderInfo。setMemberID(1L);orderInfo。setOrderType(100);LOG。info(第一版代码:无设计模式);orderService。pay(orderInfo);}}运行结果
  案例解析
  这版代码没有采用设计模式,仅仅考虑如何完成现有的业务功能,当有新的垂直业务部门增加时,需要在订单服务类中(OrderService)硬编码,增加对新业务的支持。
  下面我们采用观察者模式进行代码重构。第二版代码:基于观察者模式类图
  新增接口和类:
  IOrderServiceV2:被观察者角色,订单服务接口
  OrderServiceV2:被观察者角色,订单服务实现类
  IBusinessService:观察者角色,垂直业务服务接口(第一版代码)
  BookBusinessService:观察者角色,图书业务服务实现类(第一版代码)
  MobileBusinessService:观察者角色,手机业务服务实现类(第一版代码)
  IOrderServiceV2:订单服务接口
  为了动态增加垂直业务,这个接口增加了三个方法,用于增加,删除和通知垂直业务部门的实现类。packagecom。geekarchitect。patterns。observer。demo02;importcom。geekarchitect。patterns。observer。demo01。IBusinessService;importcom。geekarchitect。patterns。observer。demo01。OrderInfo;author极客架构师吴念createTime20221117publicinterfaceIOrderServiceV2{voidpay(OrderInfoorderInfo);voidaddBusinessService(IBusinessServicebusinessService);voidremoveBusinessService(IBusinessServicebusinessService);voidnotifyAllBusinessService(OrderInfoorderInfo);}OrderServiceV2:订单服务实现类
  这个类的businessServiceList属性,它在观察者模式中,属于缓冲池角色。在简易的观察者模式中,缓冲池角色,通常由被观察者角色来管理。这个缓冲池用来存储当前被观察者对应的所有观察者角色对象。packagecom。geekarchitect。patterns。observer。demo02;importcom。geekarchitect。patterns。observer。demo01。BookBusinessService;importcom。geekarchitect。patterns。observer。demo01。IBusinessService;importcom。geekarchitect。patterns。observer。demo01。MobileBusinessService;importcom。geekarchitect。patterns。observer。demo01。OrderInfo;importorg。slf4j。Logger;importorg。slf4j。LoggerFactory;importorg。springframework。beans。factory。InitializingBean;importorg。springframework。stereotype。Service;importjava。util。ArrayList;importjava。util。List;author极客架构师吴念createTime20221117ServicepublicclassOrderServiceV2implementsIOrderServiceV2,InitializingBean{privatestaticfinalLoggerLOGLoggerFactory。getLogger(OrderServiceV2。class);privatefinalListIBusinessServicebusinessServiceListnewArrayList();Overridepublicvoidpay(OrderInfoorderInfo){LOG。info(订单中台:订单支付成功);this。notifyAllBusinessService(orderInfo);}OverridepublicvoidaddBusinessService(IBusinessServicebusinessService){businessServiceList。add(businessService);}OverridepublicvoidremoveBusinessService(IBusinessServicebusinessService){}OverridepublicvoidnotifyAllBusinessService(OrderInfoorderInfo){LOG。info(订单中台:迭代通知所有的垂直业务部门);businessServiceList。stream()。forEach(businessServicebusinessService。doService(orderInfo));}OverridepublicvoidafterPropertiesSet()throwsException{LOG。info(订单中台:初始化所有的垂直业务部门);this。addBusinessService(newBookBusinessService());this。addBusinessService(newMobileBusinessService());}}运行结果
  案例解析
  这版代码,我们基于观察者模式对第一版代码进行了重构,主要是在订单服务类中,增加了存储垂直业务部门的集合,以及增加,删除和通知垂直业务部门的方法。这样当垂直业务部门发生变化时,可以方便的增加和删除相应的部门,扩展性得到了提高。
  那么这版代码有没有其他问题呢,如果仅仅从观察者模式的常规实现看,应该是可以了,但是,其实这版代码,是有问题的,而且问题还非常严重,在多线程环境下,会发生消息重复,漏发等问题。
  待会对比第三版代码,大家就能意识到问题的严重性。
  下面我们先了解一下观察者模式的概念。观察者模式定义解析Defineaonetomanydependencybetweenobjectssothatwhenoneobjectchangesstate,allitsdependentsarenotifiedandupdatedautomatically。
  Gof《DesignPatterns:ElementsofReusableObjectOrientedSoftware》
  定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
  Gof《设计模式:可复用面向对象软件的基础》
  观察者模式的定义,我认为定义的比较好,翻译的也比较好,通俗易懂,没啥弯弯绕的东东,也没有陌生的术语。如果非要说它里面的关键词,我认为是关系和变化。
  关系
  什么关系?依赖关系。
  依赖关系在观察者模式的重要程度,还体现在,观察者设计模式还有另外一个别名(观察者模式有好几个名字,我们后面都会提到),就叫Dependents。
  什么是依赖关系?好像码农都知道吧。
  就好像儿子和父亲的关系,儿子和父亲之间就是依赖关系,儿子是依赖方,父亲是被依赖方,儿子在成年之前,不能独立生活,需要父亲的抚养。(哦,我只有一个女儿,举女儿和父亲的关系更好一些,但是我感觉,依赖关系是反着的,小家伙刚刚会跑,一天不见,我就感觉,杯子里的咖啡没有了滋味,两天不见,电脑旁绿植的颜色,就淡了,三天不见,脑袋里一片空白,索性赋诗一首)。码农的女儿情你就是我的世界未有你时,以为拥有整个世界。
  走向产房,害怕失去整个世界。
  捧你在怀,方知你才是我的整个世界。
  一日不见你,杯子中Java的苦味,淡了。
  两日不见你,电脑旁Oak的颜色,褪了。
  三日不见你,脑袋里Java的技巧,忘了。
  只要能看你,哪怕一分秒,
  我愿接受千百行代码bug的洗礼。
  只要能抱你,哪怕一厘秒,
  我愿承受万千次系统上线的失利。
  只要能亲你,哪怕一毫秒,
  我愿遭受亿万个黑客的攻击。
  你,就是我的整个世界。
  吴念,2022年11月22日
  (刚开始学写诗,写的不好,请大家见谅,另外,我虽然天天喝美式咖啡,但是没有喝过Java咖啡,我的电脑旁也没有oak树,只是因为它们都和我们的Java语言有关。)
  对于依赖关系,最重要的是搞清楚,谁是依赖方,谁是被依赖方,在没有解耦合的依赖关系中,依赖方不能独立存在,被依赖方则反之。
  所有研究依赖关系的设计模式,宗旨都包括解耦合依赖双方,干掉依赖关系,或者降低依赖度。
  从面向对象的角度看,对象和对象之间,一个对象调用了另外一个对象的方法,或者访问了另外一个对象的属性,这两个对象之间就存在依赖关系。
  怎么样的依赖关系?
  再给依赖关系加个定语,一对多的依赖关系
  什么是一对多我就不多说了,这里面要强调的,就是一对多里面的一,往往是被依赖方,多则是依赖方。
  变化
  谁的变化?对象状态的变化,也就是对象属性的变化。
  被依赖方对象状态的变化,变化的后果呢,就是所有依赖于它的对象,都会得到通知,并自动更新。
  总结一下,观察者模式,就是定义对象之间的依赖关系,当被依赖方(也就是一方)状态发生变化时,所有依赖方(也就是多方),都会得到通知并被自动更新。
  这个定义后面几个字并被自动更新,不太严谨,依赖方都会得到通知一般情况下是没问题的,但是,是否被自动更新就不得而知了,是否需要处理这个通知,这个要看依赖方的业务需求。第三版代码:基于JDK内置的观察者模式JDK中的观察者模式
  JDK对于观察者模式,可谓非常重视,从JDK1。0开始就提供了支持,是由Java创始人之一的ChrisWarth设计实现的。
  Fromlefttorighttheyare:AlFrazier,JoePalrang,MikeSheridan,EdFrank,DonJackson,FayeBaxter,PatrickNaughton,ChrisWarth,JamesGosling,BobWeisblatt,DavidLavalleeandJonPayne。Missinginaction:CindyLong,ChuckClanton,SheuelingChangandCraigForrest。
  上图是Java初创团队的全家福,Java创始人JamesGosling大家应该都能认出来,ChrisWarth就在他的左边,具体是哪个,就不得而知了,估计是戴眼镜那个。类图
  类与接口
  java。util。Observer:接口,观察者角色
  java。util。Observable:类,被观察者角色
  原著中的观察者模式
  上图,是GOF在其原著中对设计模式的结构定义,ChrisWarth在设计和实现JDK的观察者模式时,基本上尊重了原著。Observer几乎一模一样。而原著中的Subject(主题),被命名为了Observable(被观察者),我喜欢这个名字,语义比较连贯,这也是我为什么在用REIS模型分析观察者设计模式时,把它里面的角色定义为观察者角色(Observer)和被观察者角色(Observable)的原因。java。util。Observer:接口,观察者角色
  作为观察者角色,它里面只有一个update方法。packagejava。util;AclasscanimplementthecodeObserverinterfacewhenitwantstobeinformedofchangesinobservableobjects。authorChrisWarthseejava。util。ObservablesinceJDK1。0publicinterfaceObserver{Thismethodiscalledwhenevertheobservedobjectischanged。AnapplicationcallsanObservableobjectsnotifyObserversmethodtohavealltheobjectsobserversnotifiedofthechange。paramotheobservableobject。paramarganargumentpassedtothenotifyObserversmethod。voidupdate(Observableo,Objectarg);}codejava。util。Observable:抽象类,被观察者角色
  下面是Observable的类注释和源代码,大家可以的看一下,特别是出现synchronized关键字的地方,就会明白,我们在第二版实现的代码,虽然理论上正确,但是在真正的生产环境中,特别是在多线程环境中,是多么的不靠谱。
  另外,这个类中,存储观察者的集合,为什么没有使用我们常见的List,而是用Vector,看过我上期的文章,就应该知道,在JDK1。0的时候,List集合还没有诞生。
  对于多线程相关的内容,我在后面的《Java极客之并发编程》中,会给大家进行分享,敬请期待。
  另外,还有一个要注意的地方,就是这个类的注释部分。
  Thisclassrepresentsanobservableobject,ordatainthemodelviewparadigm。
  从这句话,可以看出观察者模式,与我们熟悉的MVC设计模式,有着千丝万缕的联系。Observable就相当于Model,而Observer,就相当于View。
  packagejava。util;publicclassObservable{privatebooleanchangedfalse;privateVectorObserverobs;ConstructanObservablewithzeroObservers。publicObservable(){obsnewVector();}Addsanobservertothesetofobserversforthisobject,providedthatitisnotthesameassomeobserveralreadyintheset。Theorderinwhichnotificationswillbedeliveredtomultipleobserversisnotspecified。Seetheclasscomment。paramoanobservertobeadded。throwsNullPointerExceptioniftheparameteroisnull。publicsynchronizedvoidaddObserver(Observero){if(onull)thrownewNullPointerException();if(!obs。contains(o)){obs。addElement(o);}}Deletesanobserverfromthesetofobserversofthisobject。PassingCODEnulltothismethodwillhavenoeffect。paramotheobservertobedeleted。publicsynchronizedvoiddeleteObserver(Observero){obs。removeElement(o);}seejava。util。ObservableclearChanged()seejava。util。ObservablehasChanged()seejava。util。Observerupdate(java。util。Observable,java。lang。Object)publicvoidnotifyObservers(){notifyObservers(null);}Ifthisobjecthaschanged,asindicatedbythehasChangedmethod,thennotifyallofitsobserversandthencalltheclearChangedmethodtoindicatethatthisobjecthasnolongerchanged。
  Eachobserverhasitsupdatemethodcalledwithtwoarguments:thisobservableobjectandtheargargument。paramarganyobject。seejava。util。ObservableclearChanged()seejava。util。ObservablehasChanged()seejava。util。Observerupdate(java。util。Observable,java。lang。Object)publicvoidnotifyObservers(Objectarg){atemporaryarraybuffer,usedasasnapshotofthestateofcurrentObservers。Object〔〕arrLocal;synchronized(this){WedontwanttheObserverdoingcallbacksintoarbitrarycodewhileholdingitsownMonitor。ThecodewhereweextracteachObservablefromtheVectorandstorethestateoftheObserverneedssynchronization,butnotifyingobserversdoesnot(shouldnot)。Theworstresultofanypotentialraceconditionhereisthat:1)anewlyaddedObserverwillmissanotificationinprogress2)arecentlyunregisteredObserverwillbewronglynotifiedwhenitdoesntcareif(!changed)return;arrLocalobs。toArray();clearChanged();}for(intiarrLocal。length1;i0;i)((Observer)arrLocal〔i〕)。update(this,arg);}Clearstheobserverlistsothatthisobjectnolongerhasanyobservers。publicsynchronizedvoiddeleteObservers(){obs。removeAllElements();}MarksthisObservableobjectashavingbeenchanged;thehasChangedmethodwillnowreturntrue。protectedsynchronizedvoidsetChanged(){changedtrue;}Indicatesthatthisobjecthasnolongerchanged,orthatithasalreadynotifiedallofitsobserversofitsmostrecentchange,sothatthehasChangedmethodwillnowreturnfalse。ThismethodiscalledautomaticallybythenotifyObserversmethods。seejava。util。ObservablenotifyObservers()seejava。util。ObservablenotifyObservers(java。lang。Object)protectedsynchronizedvoidclearChanged(){changedfalse;}Testsifthisobjecthaschanged。returntrueifandonlyifthesetChangedmethodhasbeencalledmorerecentlythantheclearChangedmethodonthisobject;falseotherwise。seejava。util。ObservableclearChanged()seejava。util。ObservablesetChanged()publicsynchronizedbooleanhasChanged(){returnchanged;}ReturnsthenumberofobserversofthisObservableobject。returnthenumberofobserversofthisobject。publicsynchronizedintcountObservers(){returnobs。size();}}code
  我们看看如何基于JDK里面的观察者模式,重构第一版代码,注意,是第一版代码,不是第二版代码。类图
  类及接口
  OrderServiceV3Observable:被观察者角色,订单服务类
  BookBusinessServiceV3:观察者角色,图书业务服务实现类
  MobileBusinessServiceV3:观察者角色,手机业务服务实现类OrderServiceV3Observable:被观察者角色,订单服务类
  这个类的代码,因为继承了Observable,所以比第二版的订单服务类精简了不少。packagecom。geekarchitect。patterns。observer。demo03;importcom。geekarchitect。patterns。observer。demo01。OrderInfo;importorg。slf4j。Logger;importorg。slf4j。LoggerFactory;importorg。springframework。beans。factory。InitializingBean;importorg。springframework。context。annotation。Scope;importorg。springframework。stereotype。Service;importjava。util。Observable;author极客架构师吴念createTime20221117ServiceScope(prototype)publicclassOrderServiceV3ObservableextendsObservableimplementsInitializingBean{privatestaticfinalLoggerLOGLoggerFactory。getLogger(OrderServiceV3Observable。class);publicvoidpay(OrderInfoorderInfo){LOG。info(订单中台:订单支付成功);this。setChanged();修改状态this。notifyObservers(orderInfo);通知观察者}OverridepublicvoidafterPropertiesSet()throwsException{LOG。info(订单中台:初始化垂直业务部门);this。addObserver(newMobileBusinessServiceV3());this。addObserver(newBookBusinessServiceV3());}}BookBusinessServiceV3:观察者角色,图书业务服务实现类packagecom。geekarchitect。patterns。observer。demo03;importcom。geekarchitect。patterns。observer。demo01。OrderInfo;importorg。slf4j。Logger;importorg。slf4j。LoggerFactory;importjava。util。Observable;importjava。util。Observer;手机业务服务author极客架构师吴念createTime20221117publicclassBookBusinessServiceV3implementsObserver{privatestaticfinalLoggerLOGLoggerFactory。getLogger(BookBusinessServiceV3。class);Overridepublicvoidupdate(Observableo,Objectarg){OrderInfoorderInfo(OrderInfo)arg;LOG。info(垂直业务部门:图书业务服务,订单信息{},orderInfo);}}
  运行结果
  案例解析
  这版代码,我们基于JDK的观察者模式模块来实现,所以省去了很多工作,对于Observable类,我们要注意两点。
  1,因为java是单一继承制,所以一旦继承了Observable,我们的类,就不能再有自己的父类。可以考虑用组合的方式试试。
  2,Observable类是有状态的,需要先调用setChanged(),再调用notifyObservers(orderInfo)。
  下面,我们基于REIS模型,对观察者模式进行解析。REIS模型分析观察者模式
  REIS模型是我总结的分析设计模式的一种方法论,主要包括场景(scene),角色(role),交互(interaction),效果(effect)四个要素。角色(Role)
  角色,一般为设计模式出现的类,或者对象。每种角色有自己的职责。
  在观察者模式中,原著中定义了四种角色,如下图所述,分别是观察者接口(Observer)和观察者具体类(ConcreteObserver)和主题接口(Subject)和主题具体类(ConcreteSubject)。
  以我的观点,在这个设计模式中,区分角色的接口和具体类,没有实际意义。所以,我认为,观察者模式,有四种角色,分别是观察者角色(ObserverRole),被观察者角色(ObserableRole),消息角色(Messagerole),以及缓冲池(cachepoolrole)。
  观察者角色(ObserverRole)
  观察者角色(在观察者模式的各种化身中,也可以称为订阅者角色(subscriberole),监听器角色(Listenerrole),模型角色(Modelrole),消费者角色(consumerrole))。
  它在依赖关系中属于依赖方,在一对多关系中,属于多方,它的职责就是,在被观察者角色发生变化时,根据业务需要,执行自己的业务逻辑。所以这个角色里面的方法比较简单,一般只需要定义自己的业务方法即可。
  观察者模式的各种化身,我们后面有详细说明。
  被观察者角色(ObserableRole)
  被观察者角色,(在观察者模式的各种化身中,也可以称为发布者角色(Publishrole),事件源角色(eventsourcerole),模型角色(Viewrole),生产者角色(producerrole))。
  它在依赖关系中属于被依赖方,在一对多关系中,属于一方,它的职责可以从两方面考虑。
  一方面,就是有自身的业务逻辑,并造成一定的状态变化或者事件发生。
  另一方面,也是更为重要的一方面,就是要把状态变化或者事件发生,通知出去,或者叫发布出去,让所有的观察者都知道这件事。
  至于如何做,至少可以有两个选择。
  1,被观察者自己逐个通知观察者,这样双方耦合度高。(我们上面的案例代码就是这种方式)
  2,把消息发送给,或者委托给第三方,也就是消息缓冲池(消息系统),然后由消息缓存池,负责将消息发布出去,这样观察者与被观察者,就彻底解耦合了。
  如果采用的是第一种方案,被观察者角色里面,就需要定义增加,删除和通知观察者的相应方法。
  如果采用第二种方案,就意味着,需要使用专业的消息系统或者叫消息引擎了,如ActiveMQ,Kafka,RocketMQ等等。
  消息角色(Messagerole)
  消息角色,顾名思义,就是封装消息的对象,(在观察者模式的各种化身中,它也可以称为事件(Event),实体类(Entity),产品(product)等等)。它的职责简单,就不多说了。
  缓冲池角色(cachepoolrole)
  缓冲池角色,是用来解耦合观察者和被观察者的角色,这个角色简单一些的,可以是集合类,稍微复杂一些的,就是消息系统或者叫消息引擎了。
  要注意缓冲的内容,可以是消息,也可以是观察者角色,在简单的观察者模式中,通常是缓冲观察者,而在消息引擎中,通常是缓冲消息或者事件的。
  要实现一个高效的,确保消息不漏发,不重发,而且要支持各种类型的消息,不是一件容易的事情。这个我们在后面的专题分享中,会给大家介绍。交互(interaction)
  交互,是指设计模式中,各种角色是如何交互的,一般用UML中的序列图,活动图来表示。简单的说就是角色之间是如何配合,完成设计模式的使命的。
  对于观察者模式,角色之间的交互,可以分为两个阶段:
  1,初始化阶段
  1,可以由被观察者将依赖自己的所有观察者,加入到缓冲池集合中。
  2,或者观察者角色,在缓冲池角色,注册相应的消息和事件(消息系统中的流程)
  2,运行阶段
  1,被观察者角色,在事件发生后,封装消息实体。
  2,如果存在消息缓冲池,被观察者就将消息发送到消息缓冲池中,反之,则直接逐个发送给观察者角色。
  3,观察者角色,在接到通知后,根据消息信息,判断是否需要处理消息,根据自己的业务逻辑,进行业务处理。场景(Scene)
  场景,也就是我们在什么情况下,遇到了什么问题,需要使用某个设计模式。
  对于观察者模式,由于它有很多化身,如MVC模式,发布订阅模式(PublishSubscribe),事件监听机制,生产者消费者模式,所以使用的场景非常多。大家在工作中接触的比较多的,应该有web项目的MVC架构,多线程里面的线程池,javaSwing里面的事件监听,还有就是消息系统。效果(effect)
  效果,使用该设计模式之后,达到了什么效果,有何意义,当然,也可以说说它的缺点,或者风险。
  从我们前面的案例可以看出,解释器模式达到了以下效果。
  对于观察者模式,我认为它的优点,可以完全覆盖它的缺点,所以,我只说它的优点。
  它的最大优点,就是通过消息缓冲池,实现了观察者和被观察者的完全隔离,这也是它能在分布式与微服务架构的今天,能成为中流砥柱的原因。
  总结一下,基于REIS分析模型,观察者模式由四个角色组成,分别是观察者角色(ObserverRole),被观察者角色(ObserableRole),消息角色(Messagerole),以及缓冲池角色(cachepoolrole),它的宗旨是,通过缓冲池角色,实现观察者与被观察者的解耦合。
  观察者模式的化身
  在GOF原著中,观察者模式,除了正式名称Observer之外,还有两个别名,就是Dependents和PublishSubscribe。
  Dependents除了名字,没有其他辅助文献资料,对其进行详细的阐述,来龙去脉无从考究(有知道的可以评论区留言,或者私信我,重重有赏哦)。发布订阅模式(PublishSubscribe)
  而PublishSubscribe,就是大名鼎鼎的发布订阅模式,常常现身于各种消息引擎系统中,如老牌的ActiveMQ,炽手可热的Kafka,阿里系的RocketMQ等等。关于发布订阅模式,我会在分布式异步消息框架的相关分享中详细介绍。
  而同时和发布订阅模式,出现在消息系统的,还有另外一个设计模式,就是生产者消费者模式。生产者消费者模式
  生产者消费者模式,通常作为并发编程的一种设计模式,它的起源,是有据可查的,它源自艾兹格迪科斯彻的一篇论文。Weconsidertwoprocesses,whicharecalledtheproducerandtheconsumerrespectively。Theproducerisacyclicprocessthatproducesacertainportionofinformation,thathastobeprocessedbytheconsumer。Theconsumerisalsoacyclicprocessthatneedstoprocessthenextportionofinformation,ashasbeenproducedbytheproducerWeassumethetwoprocessestobeconnectedforthispurposeviaabufferwithunboundedcapacity。
  艾兹格迪科斯彻,荷兰的计算机科学家,72年获得图灵奖,被誉为结构程序设计之父,他的成就很多,说几个大家可能比较熟悉的,他提出了goto有害论,他解决了著名的哲学家聚餐问题,也就是多线程的问题。
  关于生产者消费者模式,我会在即将推出的《java极客之并发编程》中的线程池部分,进行详细讲解。MVC模式
  什么是MVC(Modelviewcontroller),我就不多说了,为什么说观察者模式隐身于MVC模式之中呢。
  在GOF的原著中,列举的观察者模式的案例,就是一个典型的MVC模式的案例,同一个数据源,显示在不同的视图组件中(表格,柱状图,饼图)。
  三个不同的视图View(Observer角色),显示同一个模型Model的数据(Observable角色)。
  作者在原文中对MVC与观察者模式的关系,阐述如下所示。
  ThefirstandperhapsbestknownexampleoftheObserverpatternappearsinSmalltalkModelViewController(MVC),theuserinterfaceframeworkintheSmalltalkenvironment〔KP88〕。MVCsModelclassplaystheroleofSubject,whileViewisthebaseclassforobservers。
  MVC设计模式中的视图(View)对应观察者模式中的Observer,模型(Model)对应主题(Subject)(也就是我们所说的被观察者Observable)事件监听机制(EventListener)
  事件监听机制,大部分java程序员接触的比较少,熟悉javaswing的朋友,应该会有所了解。那它和观察者模式,有什么关系呢?
  观察者,英语是observer,可以翻译为观察者,目击者;观察家。
  但是,不论是观察者,目击者好像都是用眼睛的,用耳朵就不能观察了吗,对于我这种被国产抗日神剧和谍战剧洗礼的老码农,谍战教父柳云龙的忠实粉丝,谍战剧中我党的卧底人员,潜入中统或者军统内部,用窃听器窃听敌人秘密会议的镜头还历历在目,如果用耳朵听,也是观察,那么listener和Observer,是不是就殊途同归了呢。
  好像是胡说八道,牵强附会啊。
  这个还真不是我忽悠大家,大家在百度搜一下事件监听机制观察者模式,在Bing国际版搜索eventlistenerobserver,也可以看到不少相关文章,不过,关于这两个设计模式,特别权威的文章,我目前还没有发现,有知道的,可以私信我,或者评论区晒一晒哦。
  我的观点,观察者模式,与MVC模式,事件监听模式,发布订阅模式,生产者消费者模式,虽然名字各异,但是本质相同,不同的业务场景,各有侧重,殊途同归,貌离神合。
  大家怎么看,可以评论区留言。总结如下
  通用代码和类图
  对于解释器这种级别的设计模式,通用代码没有任何实际价值,来参考价值都没有,所以,我就提供了。大家如果感兴趣,可以到网上搜搜,借鉴一下。
  接口及类
  IObservable:被观察者接口
  AbstractObservable:被观察者抽象类
  ConcreteObservableA:被观察者实现类
  IObserver:观察者接口
  ConcreteObserverA:观察者实现类A
  ConcreteObserverB:观察者实现类B
  Message:消息
  相关代码,大家到我的github上下载即可。
  下期预告
  观察者模式,我们就分享到这里,我们今天重点讲解了观察者模式的真身,对于它的其他各种化身,我在后面的相关专题中,会一一详细分享,敬请期待。下期,我们将分享最后一个行为型设计模式中介者模式(MediatorPattern)。
  极客架构师,专注架构师成长。
  关注我,我将持续分享更多架构师的相关文章和视频,我们下期见。

澳网18决赛连爆4大冷门,女单世界第一爆冷出局,男单黑马又赢了1月22日,澳大利亚网球公开赛单打18决赛开打,女单世界第一斯瓦泰克02爆冷不敌雷巴金娜,无缘女单八强。男单比赛中,大黑马科达继续黑马本色,32击败了10号种子胡尔卡奇,令人十分惊中国男足还有希望!国际足联主席表态2026年或有惊喜,球迷泼冷水男足世界杯还是那么让人热血沸腾,可回头看看国足,真是一肚子火。目前,关于李铁的调查还没有结束,原足协高层刘奕和陈永亮也被带走了。看着这一地鸡毛,大部分国人都彻底失望了,甚至喊出取消科比81分纪念日托马斯布莱恩特3114湖人25分大逆转胜开拓者!NBA常规赛1月23日继续进行,今天是科比对阵猛龙狂砍81分17周年纪念日!最终,湖人以121112战胜开拓者,开拓者遭遇3连败!首节开始,托布上来独得8分帮助湖人打出184开局!20!澳网爆大冷30岁老将淘汰4号种子,中国选手再次传来喜讯北京时间2023年1月23日,澳网进入第8个比赛日,女单第四轮再次爆出冷门,30岁的波兰老将里内特20淘汰4号种子加西亚,职业生涯首次跻身大满贯女单八强。中国金花方面,虽然老将张帅00后选手在2023年澳网集体爆发了2023年澳网开幕之前,豆粉曾言,一出三国演义的好戏将在2023年澳网隆重上演以资深国王纳达尔小德领军的85后选手以年轻国王梅德维德夫领军的95后选手以新晋国王阿尔卡拉斯领军的004连败!杜兰特缺阵,篮网不赢球,欧文领导力欠缺,西蒙斯又低迷篮网队不出意外的败给了情况糟糕的太阳队,没有了杜兰特,篮网队迅速坠落为联盟最烂的球队,在阿杜缺阵的4场比赛中,篮网队保持不胜,本来一片大好的形势也迅速恶化,目前他们的战绩已经和第五失误大战热火笑到最后,麦科勒姆错失绝杀鹈鹕四连败在一场失误大战中,鹈鹕领先了一整晚,天亮时输掉比赛,新秀戴森丹尼尔斯最后20秒连续两次失误,成为致命伤,CJ也错失了一记本可以杀死比赛的三分球,鹈鹕最终96100吞下四连败。泰勒希范乔丹28分巴雷特30分兰豆2319猛龙3人20送尼克斯4连败直播吧1月22日讯NBA常规赛继续进行,猛龙主场迎战尼克斯,两队目前状态都不佳,且都经历3连败,而之前两队在纽约麦迪逊广场有过交锋,当时猛龙加时涉险取胜。比赛开始,猛龙捍卫主场打得奥斯卡再谈加盟上港,徐新之事再被提起,泰山队当初有多无奈目前,中超联赛正处于冬歇期,足协忙于清算各家俱乐部的薪资状况,而各支球队则正在为新赛季的规划而努力筹划。球员们在结束了上赛季的联赛之后,终于迎来了难得的休息时间。在这个间隙,上海海像有一双无形的手,羽毛球印度公开赛,世界第一全部落马至1月22日,世界羽毛球共完成了两站比赛,结果非常规整,第一站世界第一全部夺冠,第二站世界第一全部落马,背后像有一双无形的手。第一站马来西亚公开赛,安赛龙夺得男单冠军,山口茜夺女单新时代,我在中国江苏女子手球队主教练金甲洙,曾是韩国男子手球队员。退役后,他曾先后出任韩国新西兰中国等国家队主教练。在世界各地兜兜转转了一圈,金甲洙说,他最爱的还是中国。2014年,金甲洙应江苏省
美国非要把中国逼入战争的实质是什么作者悟道永生中美战争,一触即发现在已经到了不是打不打,而是怎么打。拜登在受许多媒体采访回答时,对于被问及是否会使用武力干涉台岛事务的问题,拜登给出的回答是肯定的。美国空军太平洋司令有哪些价格不贵,喝完不上头不难受的白酒?这3款是老酒友钟爱经常喝酒的朋友都有过喝完酒上头的感受,甚至酒后几天都浑身难受,而通常是2个原因导致的,一个原因是一兴奋喝得太多了,还有一个原因是酒的品质不太好,含有很多杂质,比如甲醇杂醇杂醛等成分颜值与实力并存!14位投研女神现身说法,支撑她们坚持下去的理由是又到一年一度的女神节!如今,女性已经成为券业重要的投研力量。证券投研工作向来以高强度的工作节奏与巨大的压力出名,一路走来,在勤奋专业目标清晰高效等标签的背后,那些女性分析师女性基金这4个提案,没有掌声反遭一片群嘲,网友不如不提!两会代表,有的提案是真的实实在在,有调研有反映群众心声,引来点赞。但是有的提案却纸上谈兵,不痛不痒,迎来一片哗然,被群嘲和嫌弃,白白浪费提议一次机会。国之大计,几年一次,应该迫切关以高质量公共文化服务增强人民精神力量有呼有应作者范玉刚(山东大学特聘教授)随着我国公共文化服务体系不断完善,人民群众的精神文化生活日益丰富充实。国家统计局公布的数据显示,截至2022年底,全国共有公共图书馆3303个拉塞尔复出砍28分9助攻,湖人力克猛龙获三连胜湖人主场122112击败猛龙。开场阿奴诺比波尔特尔巴恩斯连续攻筐得分,西亚卡姆命中远投,猛龙177开局。之后阿奴诺比又命中两记三分,猛龙2510领先多达15分。戴维斯中投,拉塞尔突正厅级干部,名下房产2714套,总面积43。3万平妈妈走的时候拉着我的手,叮嘱我一定要照顾好弟弟妹妹。现在我没有照顾好他们,反而把他们都领进了监狱说这句话的就是今天的主角徐长元,在央视一档反腐纪录片里说过的意味深长的话。在纪录片中拎稳菜篮子,给设施农业升级甘肃省张掖市甘州区近年来大力发展设施农业,助力乡村振兴。目前,蔬菜种植面积已达41。5万亩。图为甘州区党寨镇陈寨村的蔬菜大棚里,种植户们正忙着采摘西红柿。王将摄(人民视觉)春茬蔬菜2023年养老金上涨已经敲定!调整方法和步骤是怎样的?何时补发?2023年养老金上涨已经敲定!调整的方法和步骤是怎样的?何时补发到账?提前看看千呼万唤始出来!1。3亿退休人员翘首以盼的养老金第19次连涨,终于在阳春三月迎来了定调,许多退休人员欢千年盛世中筋脉的增长后期我们达到了120元气就可以开始升级自己的经脉。游戏一共有四条经脉,分别是任脉,督脉,带脉,冲脉,每一条经脉都会有10个穴位。每一个穴位分成金木水火土五行属性。如果将一个穴位中金千年盛世原始千年经典国风手游,再战江湖千年作为一款在2000年的网络游戏,在当时的众多游戏中也是不俗的,受到广大玩家的喜爱,作为一款中国武功和武林为背景的游戏,当时也是受到很多年轻人的喜爱在过去的时间的各式各样的游戏也
友情链接:快好找快生活快百科快传网中准网文好找聚热点快软网