行为型模式——观察者模式210914

本文深入探讨了观察者模式,解释了其在解决如看孩子等场景中的应用。观察者模式允许对象在状态改变时通知其他对象,降低了两者间的耦合。文章详细介绍了模式的构成、实现方式、优缺点,以及Java中的实现和经典应用,如Java AWT/Swing事件模型和天气管理应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Observer(观察者,Behavioral Pattern)

问题:看孩子

  • 孩子哭了,爸爸要立刻检查孩子并安慰他
  • 我们可以给爸爸单独开一个线程看着孩子 在这里插入图片描述
  • 但是这个是一个一直进行的循环,很占用CPU
  • 试了一下,真挺猛的,中间突然高的那一段就是程序运行的时候在这里插入图片描述
  • 如果我还想要妈妈也看着,也很简单,只要把爸爸代码复制一遍改个名字
  • 然后再运行
    在这里插入图片描述
  • 如果有三个孩子,根据我们的设计,一个爹/妈只能看一个娃,所以会有三个人来看这三个娃,CPU会受不了的
  • 我们要想办法!
  • 平常我们看孩子不可能一直在看,都是要听到孩子哭才去看,所以这里的想法就是不要爸爸去一直观察,而是等着娃哭,再去观察
  • 而且我们的Observer还不够nb,如果不用一直观察的话,那我一个爹就可以管一堆娃了,同理,一个娃也可以有一堆观察者
  • 这就引出了我们的观察者模式

Observer Pattern

它是啥

  • 根据我们上面的例子,针对这种情境,我们首先要有被观察的对象,我们称之为Subject,Subjects会改变状态,孩子可以哭或者高兴,商品价格可以升或降,你喜欢的姑娘可以有男朋友或者单身
  • 我们还要有观察者Observer,比如看孩子的爸爸妈妈,看商品价格的剁手党,看姑娘是否单身的你
  • 从看孩子过程中的问题我们可以发现,Observer应当是可以观察多个对象,同样一个Subject也被多个Observer观察
    • 爹妈一起看一个孩子,爹一个人也能看一堆孩子
    • 商品可以被一堆人等着降价,你也可以等着一堆漂亮衣服降价
    • 一堆人等着漂亮姑娘单身,你也可以等着一堆漂亮姑娘单身
  • 还有一个小问题,Observer不能一直看着Subject啊,生活中是浪费精力,计算机中是浪费内存
  • 我们完全可以让Subject在改变状态时自己通知Observers在这里插入图片描述
  • 以上就是观察者模式的基本构成和大致原理
  • by the way,观察者模式还被称为Dependents,Publish-Subscribe,Model-View
  • 总结来说,观察者模式就是用来维持相关的对象之间的一致性,而且是一种松耦合的方式,以下情况适用观察者模式
    • 一个抽象有两个相互依赖的方面时,可以把这两个方面单独封装,以方便重用和改变
    • 改变一个对象需要改另一个对象,而且还不知道要改几个对象时
    • 当一个对象需要能通知其他对象还不必知晓都是谁时,也就是不想要太紧的耦合时

咋实现?

  • 我直接上图啊哈哈哈哈在这里插入图片描述
    在这里插入图片描述
  • 这两个图意思都差不多,首先我们要有两个接口,之前说过几个设计原则,我们这波直接用上,面向接口编程,减小耦合,依赖倒置
  • 具体流程是这样的
    • Subject要能将Observer加入自己的观察者集合里面(也就是说Subject要有一个集合存储Observer以实现被多个观察者观察)
    • 有添加自然有删除,也就是Detach功能
    • 然后就是Subject的通知功能了,当Subject状态改变时,要调用Subject的Notify方法,遍历观察者列表中的观察者,调用它们的Update功能
    • 说到Observer的Update方法,就是字面意思,更新当前对象的状态,在看小孩的例子中,我们可以把小孩这个对象作为参数传入Update方法,由观察者来把小孩的状态改成开心的状态
  • 以上是抽象的接口,然后只要有具体的Observer和Subject的实现,观察者模式就完成了
  • 大概就这个感觉,如下图,看图别太较真,懂的都懂在这里插入图片描述

优缺点!

  • 没有银子弹,一种模式必有其优缺点
  • 好处
    • 减小了观察者和主题(被观察对象)之间的耦合
    • 可以用来支持事件广播,可以把状态改变的信息通知所有订阅的观察者
  • 不太妙的地方
    • 可能会有不期望的更新,比如你告诉喜欢的姑娘没有男朋友就告诉你,结果人家干啥都跟你说,,可能例子体会不出,但是实现观察者模式时可能会不小心就获取了一些不期待的状态更新
    • 很有可能会有错误的更新,比如状态改变和通知的顺序反了,姑娘还没分手然后她告诉你分手之后才分,可能会有一些不太好的事情发生

实现细节

  • subject可以通过集合来存储观察者
  • 当observer同时观察多个Subject时候,可以让Subject通过Update的接口告诉Observer它是谁,比如把对象传进Update
  • 触发Update的可以是Subject,也可以是观察者在触发了许多状态改变时,或者是某些第三方的对象
  • 一定要确保状态的改变在通知之前!!!
  • Subject在通知Observer自己有什么变化时有两种方式
    • Push模型,把一堆改变的数据传过去
    • Pull模型,传引用,让观察者自己找变化,
  • 观察者也可以称为Subject
  • 当观察者只想在几个状态都改变之后被通知,我们可以用一个中介对象,作为Mediator

比较出名的应用

  • Smalltalk的Model/View/Controller 用户界面框架
    • Model=Subject
    • View=Observer
    • Controller是改变Subject状态的东西
  • Java AWT/Swing的事件模型

java内置的观察者模式

  • java中实际上是有内置的观察者模式的
  • 只是由于Subject需要有一个集合来存储Observer,而接口中不能有集合,java内置的实现就把Subject直接做成一个类Observable
  • 这个内置的观察者模式已经过时了,现在只为学习需要
    在这里插入图片描述
    在这里插入图片描述
  • 我们可以看到,Observable类中有集合,相当于Subject
  • 这里的Observer的Update方法很有意思,有两个参数,一个是Subject o,一个是Object arg,可以把要传的数据封装进一个对象作为arg传进去,也就是说,这个Update可以我们之前所说的Pull和Push都实现
  • 以下为Observable的结构在这里插入图片描述
  • 可以看到add和delete观察者的方法,要注意的是notify的方法只有在setChanged被调用之后才能调用Update方法在这里插入图片描述
  • 没错,神圣的setChanged方法其实就是把changed改个值在这里插入图片描述
  • 这也就防止了之前讨论的还未修改就通知的情况发生
  • 有两个通知观察者的方法,第二个方法就是把一个封装好的数据对象Push给Observer,然后再传进update,实现了Push的模型
    • notify Observers()
    • notify Observers(Object arg)
  • 如果调用第一个就是pull的模型
  • 问题
    • Observable是一个类
    • Observable中重要的方法setChanged是被保护的,只有子类能调用,所以说,如果想调用这个setChanged只能继承它,这就违反了我们所说的用组合/聚合代替继承的原则
  • 解决方案
    • 我们可以用一个特殊的Subject包含一个Observable对象
    • 然后把这个Subject需要的Observable行为委派给这个对象

java实现标准的观察者模式

  • java内置的观察者模式不标准在它把Observable改成了类,我们依旧可以写Subject接口,在Subject的实现中放集合即可在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

实例:Java AWT/Swing中的事件模型

在这里插入图片描述

  • GUI组件可以产生GUI事件,它们是事件源,事件源就是观察者模式中的Subject
  • ActionListener,事件监听器就是观察者模式中的Observer
  • Subject要向Observer列表中注册Observer在这里插入图片描述
    在这里插入图片描述
  • AbstractButton相当于Subject接口
  • AbstractButton中fireActionPerformed方法相当于notify在这里插入图片描述
  • ActionListener相当于Observer接口
  • Listener中的actionPerformed方法相当于update在这里插入图片描述

实例:天气管理应用

在这里插入图片描述

  • 一种实现在这里插入图片描述

观察者模式实现的原则

  • 把程序中变的方面与不变的分开
    • 观察者模式中变化的内容是主题的状态以及观察者的数量和类型。使用此模式,可以改变依赖于主题状态的对象,而无需更改该主题
  • 依赖倒置原则DIP
    • 面向接口编程
  • 组合/聚合复用原则CRP
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值