观察者模式
- 定义:定义了对象之间的一对多依赖,让多个观察者对象同时监听某一个主题对象,当主题对象发生变化时,它的所有依赖者(观察者)都会收到通知并更新
- 类型:行为型
- 适用场景:关联行为场景,建立一套触发机制
- 优点:
- 观察者和被观察者之间建立一个抽象的耦合
- 观察者模式支持广播通信
- 缺点:
- 观察者之间有过多的细节依赖、提高时间消耗及程序复杂度
- 使用要得当,要避免循环调用
标准观察者模式
被观察
/** * <p>通知接口,主要干的事情,就是添加、移除观察者、以及通知所有观察者,我XXX干了什么</p> * */ public interface INotify { /** * <p>添加一个观察者</p> * @param observer 被添加的观察者对象 */ void add(IObserver observer); /** * <p>移除一个观察者</p> * @param observer 被移除的观察者对象 */ void remove(IObserver observer); /** * <p>通知所有的观察者,可以理解为,只要在监控内的对象,都通知</p> */ void notifyObservers(); /** * <p>扩展方法,可以交给主体类来实现,比如订阅系统中的源头</p> */ void extend(); } /** * <p>抽象一个主类,实现通知接口,并保留扩展方法</p> * */ public abstract class AbstractSubject implements INotify { private List<IObserver> observers = new ArrayList<>(); @Override public void add(IObserver observer) { observers.add(observer); } @Override public void remove(IObserver observer) { observers.remove(observer); } @Override public void notifyObservers() { for (IObserver observer : observers) { observer.discover(); } } } /** * <p>自定义子类,继承抽象主类,只需要干一件事情即可,通知所有观察者之前,先...</p> * */ public class MySubject extends AbstractSubject{ @Override public void extend() { System.out.println("大事不妙,有敌情!"); notifyObservers(); } }
观察者
/** * <p>观察者接口,就提供了一个discover方法</p> * */ public interface IObserver { /** * <p>发现,具体发现什么,做什么,交由实现的类吧</p> */ void discover(); } /** * <p>观察者A</p> * */ public class ObserverA implements IObserver{ @Override public void discover() { System.out.println("观察者A:不好,发现敌情,敌军还有三秒抵达战场!"); } } /** * <p>观察者B</p> * */ public class ObserverB implements IObserver{ @Override public void discover() { System.out.println("观察者B:不好,发现敌情,敌军还有两秒抵达战场!"); } }
UML
测试
public class ObserverTest { public static void main(String[] args) { // 1、抽象的、标准的、简易的通知调用 IObserver observerA = new ObserverA(); IObserver observerB = new ObserverB(); MySubject subject = new MySubject(); subject.add(observerA); subject.add(observerB); subject.extend(); /** * 百科如下描述观察者模式: * 观察者模式(有时又被称为发布/订阅模式)是软件设计模式的一种。 * 在此种模式中,一个目标对象管理所有相依于它的观察者对象,并且在它本身的状态改变时主动发出通知。 * 这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。 * * 看了解释,主要记住这句话就行了,又被称为发布/订阅模式 * 优点如下: * (1)主要或目标对象与被观察之间是完全解耦的,因此二者很容易被扩展 * (2)主对象可以将改变的状态结果通过链条的形式传递给依赖于其的所有被观察者对象, * 大大节省了通知的代码量(否则需要人工去一个个通知) * 缺点如下: * 由于观察者的数量不定,如果数量过大的话,链式的通知方法会严重影响性能!!! * 观察者和中介者模式很像,都是处理一对多的情况,但是两者是有区别的“ * 观察者模式:只能从一方到另一方循环的通知,属于单向。(好比上课,只能老实授课给班上所有的学生,反过来不行) * 中介者模式:可以从任一一方循环通知,属于双向。(好比分享会,每个人都可以分享自己的事情给别人) * 总结: * 观察者和中介者相同点: * 1:都属于行为型模式 * 2:都为了处理一对多的关系 * 3:UML实现基本相同,都有集合管理业务对象的集合,都有循环通知的方法,符合单一职责原则。 * 观察者和中介者不同点:使用场景不同,观察者属于单向,中介者属于双向。 * }
RSS消息订阅
被观察
/** * <p>消息订阅者操作接口</p> * */ public interface IRsserOperate { /** * <p>增加一个订阅者</p> * * @param rss 被增加的订阅者 */ void add(IRss rss); /** * <p>移除一个订阅者</p> * * @param rss 被移除订阅者 */ void del(IRss rss); /** * <p>根据订阅者的ID移除一个订阅者</p> * * @param rssID 被移除的订阅者的ID */ void remove(Long rssID); /** * <p>通知所有订阅者,订阅的内容是什么</p> */ void notifyRssers(String content); /** * <p>发布消息</p> */ void publish(); } /** * <p>抽象一个订阅类,实现rss操作</p> * */ public abstract class AbstractSubscription implements IRsserOperate { /** * 存放不同的消息订阅对象(一个消息订阅对象对应一个消息消费者) */ private List<IRss> rsses = new ArrayList<>(); @Override public void add(IRss rss) { this.rsses.add(rss); } @Override public void del(IRss rss) { this.rsses.remove(rss); } /** * 移除消息订阅方 * * @param rssId 消息订阅消费者ID */ @Override public void remove(Long rssId) { Iterator<IRss> iterator = rsses.iterator(); while (iterator.hasNext()) { RssUser next = (RssUser) iterator.next(); if (rssId.equals(next.getId())) { iterator.remove(); break; } } } /** * 将消息通知到所有的订阅者 * * @param content 消息内容 */ @Override public void notifyRssers(String content) { for (IRss rss : rsses) { // 循环调用 rss.rss(content); } } } /** * <p>CSDN博客系统,可以理解为目标对象,继承订阅抽象类,实现消息的发布</p> * */ public class CsdnSystem extends AbstractSubscription { /** * 订阅的消息内容 */ private String content; public CsdnSystem() { } public CsdnSystem(String content) { this.content = content; } /** * <p>一旦调用该方法,所有依赖于csdn系统的消息订阅者都将收到变更的消息</p> */ @Override public void publish() { System.out.println("博主通知:广大的童鞋们,如果对我写的文章感兴趣的话,请伸出你的大拇指,给个赞吧!"); notifyRssers(content); } }
观察者
/** * <p>消息订阅接口,提供一个订阅消息内容的实现</p> * */ public interface IRss { /** * 消息订阅,主要输出订阅的内容 * * @param content 消息内容 */ void rss(String content); } /** * <p>消息订阅消费者</p> * */ public class RssUser implements IRss { /** * 用户ID */ private Long id; /** * 用户名称 */ private String name; public RssUser(Long id, String name) { this.id = id; this.name = name; } @Override public void rss(String content) { System.out.println("尊敬的用户( id = " + this.id + ",name = " + this.name + "),你搜到了一条订阅消息:" + content); } public Long getId() { return id; }
UML
测试
public class ObserverTest { public static void main(String[] args) { String publishContent = "作者Appleyk , 刚刚发表了一篇博文:《Java 23种设计模式Demo案列大全 ☞ 【持续更新】》"; CsdnSystem csdnSystem = new CsdnSystem(publishContent); csdnSystem.add(new RssUser(10001L,"曹操")); csdnSystem.add(new RssUser(10002L,"刘备")); csdnSystem.add(new RssUser(10003L,"孙权")); // 发布消息 csdnSystem.publish(); System.out.println("=========================此时,曹操退出了群聊"); csdnSystem.remove(10001L); // 再次发布消息 csdnSystem.publish(); }
扩展Java事件监听
被观察
/** * <p> * 事件源,即事件从哪冒出来的,比如button按钮的点击事件,其事件源就是button按钮 * 事件源一定要包含事件和事件对应的监听器,支持监听器的add和事件发布操作 * 也就是,要想事件发生了,能被监正确的监听到,首先得保证有事件对象,其次事件得由事件源发布出去 * (事件不发布,你让监听者(消费者)怎么知道事件发生了!!!!!!) * </p> * */ public class MyEventSource { // 为了简单起见,直接内置一个事件 private MyEvent event; private List<IEventListener> listeners; public MyEventSource() { event = new MyEvent(this,1); listeners = Collections.synchronizedList(new ArrayList<>());; } // 添加事件监听 public void addListener(IEventListener listener){ this.listeners.add(listener); } public void setVal(int val){ event.setVal(val); publish(); } // 发布事件 protected void publish(){ for (IEventListener listener : listeners) { listener.eventChanged(event); } } } /** * <p>自定义事件</p> * */ public class MyEvent extends EventObject { private int val; public MyEvent(Object source,int val) { super(source); this.val = val; } public int getVal() { return val; } public void setVal(int val) { this.val = val; } }
观察
/** * <p>事件监听器接口</p> * */ public interface IEventListener<T> extends EventListener { // 持有一个事件对象的响应(即事件发生时,会有相应的listener执行这个方法,并对事件对象做一些业务处理) void eventChanged(T event); } /** * <p>自定义事件监听实现类</p> * */ public class MyListener implements IEventListener<MyEvent>{ @Override public void eventChanged(MyEvent event) { System.out.println("调用了eventChanged方法,event = "+event+",val = "+event.getVal()); } }
UML
测试
/** * <p>事件监听器接口</p> * */ public interface IEventListener<T> extends EventListener { // 持有一个事件对象的响应(即事件发生时,会有相应的listener执行这个方法,并对事件对象做一些业务处理) void eventChanged(T event); } /** * <p>自定义事件监听实现类</p> * */ public class MyListener implements IEventListener<MyEvent>{ @Override public void eventChanged(MyEvent event) { System.out.println("调用了eventChanged方法,event = "+event+",val = "+event.getVal()); } }
源码中的应用
- java.awt.Event:桌面程序监听器
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)