设计模式之观察者模式
一、背景
随着工作时间的越来越长,发现对设计模式缺失的坏处越来越明显,但是当你知道某种设计模式的实现方式以后,你会发现,其实工作中早已经玩过这些东西,但是你之前并不知道它属于设计模式的一种,今天就先介绍一种设计模式:观察者模式,然后我们也手动实现观察者模式以加深印象。
二、模式介绍
观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或者从属者(Dependents)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
应用场景:一个软件系统中常常要求在某一个对象的状态发生变化的时候,某些和它关联的其他的对象做出相应的改变。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合的设计方案。减少对象之间的耦合有利于系统的复用,但是同时设计时需要使这些低耦合的对象之间能够维持行为的协调一致,保证高度的协作。观察者模式是满足这一要求的各种设计方案中最重要的一种。
三、观察者模式结构和组成
由上图可知,观察者模式所涉及的角色有:
1.抽象主题角色(Subject):抽象主题角色把所有对观察者对象的引用保存在一个集合(比如Vector)里,然后每个主题都可以有任何数量的观察者。抽象主题提供的接口有:增加观察者对象、删除观察者对象、通知所有的观察者对象、获取观察者数量、设置改变。
2.具体主题角色(ConcreteSubject): 将有关状态存入具体主题对象,在具体主题的内部状态改变时,给所有注册过的观察者发送通知。
3.抽象观察者角色(Observer):为所有的具体观察者定义一个接口,订阅的主题改变时更新自己,这个接口叫做更新接口。
4.具体观察者角色(ConcreteObserver):存储与主题自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态向协调。如有需要,具体观察者角色可以保持一个指向具体主题对象的引用。
四、代码实现
1.抽象观察者角色Observer.java接口
复制package com.hafiz.www.observerPattern; /** * Desc:观察者接口 * Created by hafiz.zhang on 2017/7/27. */ public interface Observer { /** * 已订阅的主题发生改变时调用此方法来更新自己 * * @param obj 当前正在发生改变的可观察者对象 * * @param args 参数 */ void update(Observable obj, Object args); }
2.抽象主题角色Observable.java类
复制package com.hafiz.www.observerPattern; import java.util.Vector; /** * Desc: 抽象可被观察者类 * Created by hafiz.zhang on 2017/7/27. */ public abstract class Observable { private Vector<Observer> observers; // 已订阅的观察者集合 private boolean changed = false; // 状态 public Observable() { observers = new Vector<>(); } public synchronized void addObserver(Observer observer) { if (observer == null) { throw new NullPointerException(); } if (!observers.contains(observer)) { observers.addElement(observer); } } public synchronized void removeObserver(Observer observer) { observers.remove(observer); } public synchronized void setChanged() { changed = true; } public synchronized void clearChanged() { changed = false; } public void notifyObservers(Object args) { Observer[] observersLocal; synchronized (this) { if (!changed) { return; } observersLocal = new Observer[observers.size()]; observersLocal = observers.toArray(observersLocal); clearChanged(); } for (int i = 0; i < observersLocal.length; i++) { observersLocal[i].update(this, args); } } public void notifyObservers() { notifyObservers(null); } public synchronized void deleteAllObserver() { observers.removeAllElements(); } public synchronized int countObservers() { return observers.size(); } }
3.具体观察者实现类ConcreteObserver.java类
复制package com.hafiz.www.observerPattern; /** * Desc:具体观察者实现类 * Created by hafiz.zhang on 2017/7/27. */ public class ConcreteObserver implements Observer { private String status; @Override public void update(Observable obj, Object args) { System.out.println("Observable:" + obj.getClass().getName() + " status has changed"); this.status = (String)args; System.out.println("After changed ConcreteObserver`s status is : " + status); } }
4.具体主题实现类ConcreteSubject.java类
复制package com.hafiz.www.observerPattern; /** * Desc:具体主题实现类 * Created by hafiz.zhang on 2017/7/27. */ public class ConcreteSubject extends Observable { private String status = "originalStatus"; public ConcreteSubject(Observer observer) { // 注册观察者 addObserver(observer); } public void changeStatus(String status) { System.out.println("Before changed ConcreteSubject`s status is : " + this.status); this.status = status; System.out.println("After changed ConcreteSubject`s status is : " + this.status); setChanged(); notifyObservers(this.status); } }
5.单元测试类
复制package com.hafiz.www; import com.hafiz.www.observerPattern.ConcreteObserver; import com.hafiz.www.observerPattern.ConcreteSubject; import org.junit.Test; /** * Desc:设计模式demo单元测试类 * Created by hafiz.zhang on 2017/7/27. */ public class DesignPatternTest { @Test public void testObserverPattern() { ConcreteObserver observer = new ConcreteObserver(); ConcreteSubject subject = new ConcreteSubject(observer); subject.changeStatus("new Status"); } }
6.运行结果截图:
7.说明和扩展
在观察者模式中,其实又细分为推模型和拉模型,所谓推模型是指,在主题类发生改变的时候,调用观察者update()方法直接把数据通过args参数传过去,不管观察者是否真的需要;而拉模型是调用update()方法时,把自己传递给观察者,观察者需要什么数据,它自己从主题对象中获取。在我看来拉模型更加的易于扩展。
其实,在java.util包里面,jdk默认也给我们提供了这样实现,其中Observer.java是观察者抽象类,Observable.java是主题抽象类,我们可以拿来使用,其中的实现和本文的差别不大。使用时,我们只需要基于这两个类进行实现就可以了很简单。推荐使用!
文中代码github地址:https://github.com/hafizzhang/DesignPattern.git
五、总结
通过本文,我们就了解了什么叫观察者模式,以及怎么实现该模式,以及该模式的特点。以后的工作中,我们很好的结合具体场景来使用,可以说收获满满!喜欢请点赞。
感谢您花时间阅读此篇文章,如果您觉得这篇文章你学到了东西也是为了犒劳下博主的码字不易不妨打赏一下吧,让博主能喝上一杯咖啡,在此谢过了!
如果您觉得阅读本文对您有帮助,请点一下左下角“推荐”按钮,您的将是我最大的写作动力!另外您也可以选择【关注我】,可以很方便找到我!
本文版权归作者和博客园共有,来源网址:http://www.cnblogs.com/hafiz 欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利!


【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· 本地部署DeepSeek后,没有好看的交互界面怎么行!
· 趁着过年的时候手搓了一个低代码框架
· 推荐一个DeepSeek 大模型的免费 API 项目!兼容OpenAI接口!