前言
🍊作者简介: 不肯过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法解决繁琐问题,让复杂的问题变得通俗易懂。
🍊支持作者: 点赞👍、关注💖、留言💌~
大聪明在写代码的过程中发现设计模式的影子是无处不在,设计模式也是软件开发人员在软件开发过程中面临的一般问题的解决方案。大聪明本着“独乐乐不如众乐乐”的宗旨与大家分享一下设计模式的学习心得。
观察者模式
🍓🍓什么是观察者模式🍓🍓
在讲解观察者模式之前,我们先来看一下它的定义👇
观察者模式(又被称为模型(Model)- 视图(View)模式、源 - 收听者(Listener)模式、从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。即当一个对象被修改时,则会自动通知依赖它的对象。观察者模式属于行为型模式。它通常被用来实现事件处理系统。
在现实生活种很多对象都不是独立存在的,其中一个对象的行为发生改变可能会导致一个或者多个其他对象的行为也发生改变。例如,某种商品的物价上涨,会导致部分商家高兴,导致消费者伤心;当我们开车到十字路口时,看到红灯会停车,看见绿灯才会继续开车向前走;在学校里,老师听见上课铃就会去教室给学生上课,学生们也会在教室里坐好,下课铃打响后,老师和学生才会走出教室。在软件世界也是这样,比如 Excel 中的数据与折线图(或饼状图、柱状图),当数据发生改变时,对应的图也会发生改变。这些例子都是观察者模式的良好体现。通过这些例子我们也能看出观察者模式的应用场景:当我们遇到一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都需要得到通知并同时改变状态的场景时,就可以选择使用观察者模式来实现。
观察者模式共分为四个角色:
- 抽象主题(Subject): 它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
- 具体主题(Concrete Subject): 将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
- 抽象观察者(Observer): 为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
- 具体观察者(Concrete Observer): 实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
🍓🍓观察者模式的实现🍓🍓
上面我们提到了【铃声 - 老师/学生】的例子,那么我们就用观察者模式实现一下这个例子,在写代码之前我们先简单分析一下,看看应该如何实现👇
🍋第一步: 我们需要定义一个铃声事件(RingEvent)类,它记录了铃声的类型(上课铃声/下课铃声)。
🍋第二步: 定义一个学校的铃(BellEventSource)类,它是事件源,是观察者目标类,该类里面包含了监听器容器 listener,可以绑定监听者(学生或老师),并且有产生铃声事件和通知所有监听者的方法。
🍋第三步: 定义铃声事件监听者(BellEventListener)类,它是抽象观察者,其中包含了铃声事件处理方法 。
🍋第四步: 定义老师类(TeachEventListener)和学生类(StuEventListener),它们是事件监听器,也是具体观察者,听到铃声会去上课或下课。
接下来我们看看具体的代码👇
import java.util.*;/*** 观察者模式实现【铃声 - 老师/学生】的例子* @description: BellEventTest* @author: 庄霸.liziye* @create: 2022-04-12 16:58**/
public class BellEventTest {public static void main(String[] args) {/*** 铃(事件源)*/BellEventSource bell = new BellEventSource();//注册监听器(老师)bell.addPersonListener(new TeachEventListener());//注册监听器(学生)bell.addPersonListener(new StuEventListener());//打上课铃声bell.ring(true);System.out.println("-------我是分割线-------");//打下课铃声bell.ring(false);}
}
/*** 铃声事件类:用于封装事件源及一些与事件相关的参数*/
class RingEvent extends EventObject {private static final long serialVersionUID = 1L;/*** true表示上课铃声,false表示下课铃声*/private boolean sound;public RingEvent(Object source, boolean sound) {super(source);this.sound = sound;}public void setSound(boolean sound) {this.sound = sound;}public boolean getSound() {return this.sound;}
}
/*** 目标类:事件源,铃*/
class BellEventSource {/*** 监听器容器*/private List<BellEventListener> listener;public BellEventSource() {listener = new ArrayList<BellEventListener>();}/*** 给事件源绑定监听器*/public void addPersonListener(BellEventListener ren) {listener.add(ren);}/*** 事件触发器:敲钟,当铃声sound的值发生变化时,触发事件。*/public void ring(boolean sound) {String type = sound ? "上课铃" : "下课铃";System.out.println(type + "响啦!");RingEvent event = new RingEvent(this, sound);//通知注册在该事件源上的所有监听器调notifies(event);}/*** 当事件发生时,通知绑定在该事件源上的所有监听器做出反应(用事件处理方法)*/protected void notifies(RingEvent e) {BellEventListener ren = null;Iterator<BellEventListener> iterator = listener.iterator();while (iterator.hasNext()) {ren = iterator.next();ren.heardBell(e);}}
}/*** 抽象观察者类:铃声事件监听器*/
interface BellEventListener extends EventListener {/*** 事件处理方法,听到铃声*/public void heardBell(RingEvent e);
}/*** 具体观察者类:老师事件监听器*/
class TeachEventListener implements BellEventListener {@Overridepublic void heardBell(RingEvent e) {if (e.getSound()) {System.out.println("老师:上课...");} else {System.out.println("老师:下课...");}}
}/*** 具体观察者类:学生事件监听器*/
class StuEventListener implements BellEventListener {@Overridepublic void heardBell(RingEvent e) {if (e.getSound()) {System.out.println("同学们,都在教室里做好了...");} else {System.out.println("同学们,都出去玩了...");}}
}
🍎🍎执行结果🍎🍎
🍓🍓观察者模式的优、缺点🍓🍓
最后我们总结一下观察者模式的优点与缺点👇
🍌🍌优点🍌🍌
降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系(符合依赖倒置原则);目标与观察者之间建立了一套触发机制。
🍌🍌缺点🍌🍌
🍋缺点一: 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
🍋缺点二: 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
🍋缺点三: 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
小结
本人经验有限,有些地方可能讲的没有特别到位,如果您在阅读的时候想到了什么问题,欢迎在评论区留言,我们后续再一一探讨🙇
希望各位小伙伴动动自己可爱的小手,来一波点赞+关注 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●’◡’●)
如果文章中有错误,欢迎大家留言指正;若您有更好、更独到的理解,欢迎您在留言区留下您的宝贵想法。
你在被打击时,记起你的珍贵,抵抗恶意;
你在迷茫时,坚信你的珍贵,抛开蜚语;
爱你所爱 行你所行 听从你心 无问东西