观察者模式详解:核心定义与工作原理深入讲解

很多刚接触设计模式的朋友,第一次听到观察者模式的时候,都会觉得这个名字有点抽象,不知道到底是用来做什么的。其实不用怕,我们生活里天天都在接触观察者模式的逻辑,举个最简单的例子,你关注了一个博主,只要博主更新内容,所有关注他的粉丝都会收到推送。这个过程里,博主就是被观察的对象,粉丝就是观察者,这不就是观察者模式最直白的体现吗?
今天我们就把这个设计模式里最常用的观察者模式拆解开,讲清楚它的核心定义和工作原理,帮你真正搞懂什么时候该用它。
先来说核心定义,标准里的说法是,观察者模式定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会收到通知,并且自动更新。你看,刚才说的博主和粉丝的例子就完全贴合——一个博主(被观察对象)对应N个粉丝(观察者),博主更新内容(状态改变),所有粉丝都收到通知(自动更新关注内容),是不是刚好对上?
可能有人会说,这不就是消息推送吗?为什么还要专门搞一个设计模式出来?其实核心原因是解耦。我们写代码的时候,最忌讳的就是不同模块紧紧绑在一起,改一个地方到处出问题。观察者模式要做的,就是把被观察的对象和观察者分开,让两者可以独立修改和扩展,不用互相牵扯。
比如你做一个电商系统,用户下单之后,要给用户发推送短信,要通知仓库备货,还要给财务系统更新订单流水。如果不做解耦,你就会把发短信、通知仓库、更新财务这三段代码,全写进下单的方法里。哪天产品说要加一个送优惠券的功能,你又得改下单的代码,改来改去迟早出bug。
用观察者模式的话,下单逻辑只需要负责生成订单,改完状态之后发一个通知就行。发短信、备货、更新流水这些模块,都是一个个独立的观察者,自己管自己的事。以后要加送优惠券,直接加一个新的观察者就行了,下单核心代码一点都不用改,这就是解耦的好处。
讲完定义和解耦的核心优势,再来说说观察者模式里的几个核心角色,其实一共就四个,很好记。
第一个是抽象主题,也就是抽象被观察者。它就是一个接口或者抽象类,里面主要干两件事,一件是用来添加观察者,也就是让想要接收通知的对象注册进来;另一件是删除观察者,就是用户取消关注了,把它移除出去;还有一件就是通知所有观察者,状态变了,赶紧更新。
第二个是具体主题,也就是具体的被观察者。它得实现刚才说的抽象主题的方法,内部会存一个所有观察者的列表,当自己的状态发生改变的时候,就遍历这个列表,挨个发通知。还是拿博主举例子,具体主题就是这个真实的博主,他手里记着所有关注他的人,一发新内容就挨个通知粉丝。
第三个是抽象观察者,就是一个接口,里面只需要定义一个更新的方法,所有具体观察者都要实现这个方法,收到通知之后就执行这个更新方法。
第四个就是具体观察者,实现刚才说的更新方法,收到通知之后做自己该做的事,比如刚才电商例子里的短信观察者,收到下单通知就给用户发短信,仓库观察者就备货,各干各的,互不干扰。
接下来我们再讲它的工作流程,其实走一遍流程就全懂了,一点都不复杂。
第一步肯定是先注册,观察者要告诉被观察者,我要关注你,我要收你的通知。被观察者就把这个观察者放到自己的观察者列表里。比如粉丝点关注按钮,系统就把这个粉丝加到博主的粉丝列表里,流程就完成了这一步。
第二步是状态改变,被观察对象自己的状态变了,比如博主发了新文章,订单生成了,商品库存变了,这个时候具体主题就会触发通知方法。
第三步是通知,具体主题遍历自己存的所有观察者,挨个调用观察者的更新方法,把最新的状态信息传过去。
第四步就是处理,每个观察者拿到最新的状态之后,执行自己的更新逻辑,处理完就结束了。
你看,整个流程下来没有什么绕弯的地方,就是注册、变状态、发通知、处理这四步,逻辑非常清晰。
很多人会混淆推模型和拉模型,这里也简单说一下。所谓推模型,就是被观察者在发通知的时候,直接把最新的状态通过参数传给观察者,观察者拿到直接用就行。拉模型反过来,被观察者只说我变了,你要自己过来拿我最新的状态,具体拿什么观察者自己决定。
两种模式各有好坏,推模型如果传的内容太多,或者传的内容用不上,会有点影响效率。拉模式灵活一点,观察者自己按需拿,但要多一次访问,一般实际开发里,推模型用的更多一点,毕竟大部分场景下,我们都是直接把变化的内容传过去就完了。
那什么时候适合用观察者模式呢?一般来说,只要你碰到一个对象的变化,需要同步改变其他多个对象,而且你不知道具体会有多少个对象要变的时候,就直接用观察者模式准没错。
还是说刚才电商下单的例子,你不知道以后业务要加多少个后续处理,可能今天加发优惠券,明天加积分,后天加活动提醒,每次加不用改核心下单代码,加个观察者就行,扩展性非常好。
还有一种场景,就是一个对象只需要发通知,不需要知道通知发给谁,也不需要知道通知之后对方做了什么,这个时候用观察者模式就刚好,完全解耦,发完通知被观察者就不管了,处理全交给观察者。
当然,观察者模式也不是万能的,也有需要注意的地方。如果观察者特别多,遍历通知一遍其实挺费时间的,要是有观察者处理的时候出了问题,还会影响整个通知流程,所以一般大数量的场景,会考虑改成异步通知,避免卡住。
还有就是如果循环依赖的话,要小心死循环。比如A观察B,B又观察A,A一变通知B,B一变又通知A,一来二回就死循环了,写代码的时候要注意避开这种情况。
现在很多开发框架和语言库里,其实都已经内置了观察者模式的实现,不用你自己从头写所有逻辑。比如Java里自带的Observable类和Observer接口,JavaScript里的自定义事件,前端Vue里的事件总线,其实本质都是观察者模式的实现,你只要会用就行。
总的来说,观察者模式是设计模式里非常好用,也非常常用的一个模式。核心就是解决一对多依赖的解耦问题,让变化和处理分开,扩展性变好。你只要记住生活里关注博主收推送这个例子,就不会忘了它的逻辑,真的碰到需要解耦的一对多场景,直接用就可以了。

观察者模式,设计模式,观察者模式核心定义,观察者模式工作原理,抽象被观察者,抽象观察者,具体被观察者,具体观察者,推模型拉模型,解耦设计

[Q]:什么是观察者模式?
[A]:观察者模式是一种设计模式,定义了对象之间一对多的依赖关系,当被观察对象的状态发生改变时,所有依赖它的观察者对象都会收到通知并自动更新,核心作用是实现模块解耦。
[Q]:观察者模式的核心优势是什么?
[A]:观察者模式的核心优势是解耦,它将被观察对象和观察者拆分为独立模块,两者可以独立修改、扩展,新增功能时不需要改动核心业务代码,能降低模块间的耦合度,提升代码可维护性和扩展性。
[Q]:观察者模式包含哪几个核心角色?
[A]:一共包含四个核心角色,分别是抽象主题(抽象被观察者)、具体主题(具体被观察者)、抽象观察者、具体观察者,每个角色负责对应独立功能。
[Q]:观察者模式的工作流程是怎样的?
[A]:一共分为四步,首先是观察者注册到被观察者的列表中,然后被观察对象自身状态发生改变,接着被观察者遍历所有观察者发送状态变更通知,最后每个观察者收到通知后执行自己的更新处理逻辑。
[Q]:观察者模式的推模型和拉模型有什么区别?
[A]:推模型是被观察者发送通知时,直接将最新状态作为参数传给观察者,观察者直接使用即可;拉模型仅发送状态变更通知,观察者需要主动向被观察者获取最新状态,推模型更常用,拉模型灵活性更高。
[Q]:什么时候适合使用观察者模式?
[A]:当你遇到一个对象变化需要同步修改多个其他对象,且不清楚未来会新增多少个处理对象,或是一个对象只需要发送通知,不需要关心通知的接收和处理细节时,就适合使用观察者模式。
[Q]:使用观察者模式需要注意哪些问题?
[A]:如果观察者数量很多,遍历通知会耗费一定时间,此外如果出现循环依赖(A观察B,B又观察A),容易触发死循环,开发时需要避开这类问题,大数量场景可以改用异步通知提升效率。
[Q]:实际开发中有现成的观察者模式实现吗?
[A]:很多开发语言和框架都已经内置了观察者模式的实现,比如Java自带的Observable类和Observer接口,JavaScript的自定义事件,Vue的事件总线,本质都是观察者模式的落地实现。

更多观察者模式详解:核心定义与工作原理深入讲解相关问题

问题:《火影忍者》卡卡西老师在线讲解八门遁甲原理

回答:想看看大家的室内装潢 详情 >

问题:《只有神知道的世界》•慎入,里番里有捏他还真是少见么?

回答:尸王好像就没赢过几场,全靠不死和嘿嘿嘿恶心人 详情 >

问题:《暗黑3》【暗黑破坏神3卡顿详解】为什么人一多就卡,

回答:我觉得机械魔鬼出去以后不会比在门内强太多,毕竟地狱门不怎么压制高科技 详情 >

问题:《尸兄》关于魔手的问题。人有意识和脑子,意识可以理解为灵魂,意识控制

回答:小车3元/小时,大车6元/小时。6小时封顶。服务时间:09:00-17:00 详情 >

问题:《尸兄》你们觉得万物皆擒能侵住意境吗?很明显这玩意不符合物理原理,声

回答:如以前去过西湖,可去郭庄,宁静优雅,可一辺喝茶,一边欣賞西湖美景。也可以去胡雪严故居,看一下以前有錢人的住所。逛街可以去清河坊。看表演,可选印象西湖。 详情 >

share