16 观察者模式

16.1 观察者模式概述

Observer Pattern: 定义对象之间的依赖关系(一对多),当一个对象的状态发生改变时,其关联的依赖对象均收到通知并自动更新。

观察者模式又称:发布-订阅模式源-监听器模式

观察者模式结构图如下所示:

16.2 观察者模式实现

16.2.1 抽象目标类

被观察的对象,其中定义了一个 观察者 集合,提供一系列方法来增加和删除观察者对象,同时其定义了通知方法来通知观察者目标对象状态的变化。

public abstract class Subject {
protected List<Observer> observers = new ArrayList<>(); public void add(Observer observer) {
observers.add(observer);
} public void remove(Observer observer) {
observers.remove(observer);
} public abstract void notify();
}

16.2.2 具体目标类

该类实现抽象目标类的 notify 方法,同时它还实现了目标类中定义的抽象业务方法。

public class ConcreteSubject extends Subject {
private String state; public void notify() {
if (this.state.equals("changed")) {
for (Object observer : observers) {
observer.update();
}
}
}
}

16.2.3 观察者接口

观察者一般定义为接口,声明数据更新的方法。

public interface Observer {
public void update();
}

16.2.4 具体观察者

具体观察者update方法可以包含一个指向具体观察目标对象的引用

public class ConcreteObserver implements Observer {
private String state; public ConcreteObserver(String state) {
this.state = state;
} public void update() {
// 执行具体更新逻辑
}
}

16.2.5 客户端调用

public class Client {
public static void main(String[] args) {
Subject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserver("1");
Observer observer2 = new ConcreteObserver("2"); subject.add(observer1);
subject.add(observer2); // 某些状态发生变化,导致notify()方法被调用,通知所有观察者完成更新
subject.notify();
}
}

16.3 JDK中的观察者模式支持

16.3.1 java.util.Observer

该接口充当抽象观察者类,提供抽象的 update() 方法

package java.util;

/**
* A class can implement the <code>Observer</code> interface when it
* wants to be informed of changes in observable objects.
*
* @author Chris Warth
* @see java.util.Observable
* @since JDK1.0
*/
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
* <code>notifyObservers</code> method to have all the object's
* observers notified of the change.
*
* @param o the observable object.
* @param arg an argument passed to the <code>notifyObservers</code>
* method.
*/
void update(Observable o, Object arg);
}

16.3.2 java.util.Observable

该类充当抽象观察目标类(通过继承该类,扩展添加自己的业务方法),使用 Vector 存储观察对象列表,变量changed 存储观察目标是否发生改变

package java.util;

public class Observable {
// 存储变化状态
private boolean changed = false;
// 存储观察者列表,线程安全的List
private Vector<Observer> obs; /** Construct an Observable with zero Observers. */
public Observable() {
obs = new Vector<>();
} // 线程安全,添加 observer 到 obs
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
} // 删除列表中的 observer
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
} // 通知方法
public void notifyObservers() {
notifyObservers(null);
} public void notifyObservers(Object arg) {
/*
* a temporary array buffer, used as a snapshot of the state of
* current Observers.
*/
Object[] arrLocal; synchronized (this) {
/* We don't want the Observer doing callbacks into
* arbitrary code while holding its own Monitor.
* The code where we extract each Observable from
* the Vector and store the state of the Observer
* needs synchronization, but notifying observers
* does not (should not). The worst result of any
* potential race-condition here is that:
* 1) a newly-added Observer will miss a
* notification in progress
* 2) a recently unregistered Observer will be
* wrongly notified when it doesn't care
*/
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
} for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
} // 删除所有观察者
public synchronized void deleteObservers() {
obs.removeAllElements();
} protected synchronized void setChanged() {
changed = true;
} protected synchronized void clearChanged() {
changed = false;
} public synchronized boolean hasChanged() {
return changed;
} public synchronized int countObservers() {
return obs.size();
}
}

16.4 观察者模式与MVC

MVC架构模式角色分类

  • Model: 模型
  • View: 视图
  • Controller: 控制器

Model 充当观察目标,View 充当观察者,Controller 充当外部作用改变Model的状态,View则会观察到Model的改变,并更新自己的显示内容。

16.5 观察者模式优/缺点

观察者模式使用频率非常高,为实现对象之间的联动提供了一套完整的解决方案,如发布与订阅场景、监听器Listener等。

观察者模式优点

  • 观察目标只需维护一个抽象观察者集合,无需了解具体观察者的实现,降低耦合性
  • 观察者模式支持广播通信,可以向所有已注册的观察者发送通知,简化一对多系统设计

观察者模式缺点

  • 注册观察者时需要获取到观察目标对象才能完成注册
  • 如果一个观察目标有非常多观察者,轮询通知所有观察者开销会较大

参考文章

  1. 设计模式-刘伟

Java设计模式 —— 观察者模式的更多相关文章

  1. java设计模式--观察者模式(Observer)

    java设计模式--观察者模式(Observer) java设计模式--观察者模式(Observer) 观察者模式的定义: 定义对象间的一种一对多的依赖关系.当一个对象的状态发生改变时,所有依赖于它的 ...

  2. 【设计模式】Java设计模式 - 观察者模式

    [设计模式]Java设计模式 - 观察者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 @一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长 ...

  3. JAVA 设计模式 观察者模式

    用途 观察者模式 (Observer) 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象. 这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 观 ...

  4. Java设计模式--观察者模式到监听器

    观察者模式是对象的行为模式.又叫做发布-订阅模式.模型-视图模式.源-监听器模式. 抽象主题角色:主题角色将所有对观察者对象的引用到保存在一个集合里,每个主题都可以拥有任意数量的观察者.抽象主题提供一 ...

  5. Java设计模式の观察者模式(推拉模型)

    目录: 一.观察者定义 二.观察者模式的结构(推模式实现) 三.推模型和拉模型(拉模式实现) 四.JAVA提供的对观察者模式的支持 五.使用JAVA对观察者模式的支持(自带推模式实现实例) 一.观察者 ...

  6. Java设计模式 - 观察者模式

    定义 观察者模式属于对象行为型模式. 在对象之间定义一对多的依赖,这样一来当一个对象改变状态,依赖它的对象都会收到通知并自动更新. 优点 1.  主题和观察者之间抽象耦合.无论什么对象主要实现了特定的 ...

  7. 我的Java设计模式-观察者模式

    相信大家都有看过<喜洋洋与灰太狼>,说的是灰太狼和羊族的"斗争",而每次的结果都是灰太狼一飞冲天,伴随着一句"我还会回来的......".为灰太狼感 ...

  8. java设计模式-观察者模式学习

    最近学习了设计模式中的观察者模式,在这里记录下学习成果. 观察者模式,个人理解:就是一个一对多模型,一个主体做了事情,其余多个主体都可以观察到.只不过这个主体可以决定谁去观察他,以及做什么事情可以给别 ...

  9. Java设计模式——观察者模式(事件监听)

    最近在看Tomcat和Spring的源码,在启动的时候注册了各种Listener,事件触发的时候就执行,这里就用到了设计模式中的观察者模式. 引-GUI中的事件监听 想想以前在学Java的GUI编程的 ...

  10. JAVA设计模式—观察者模式和Reactor反应堆模式

    被观察者(主题)接口 定义主题对象接口 /**抽象主题角色: 这个主题对象在状态上发生变化时,会通知所有观察者对象 也叫事件对象 */ public interface Subject { //增加一 ...

随机推荐

  1. 容器内Java微服务报错:unable to create new native thread

    unable to create new native threadhttps://stackoverflow.com/questions/16789288/java-lang-outofmemory ...

  2. 07.异常、多线程、Lambda 表达式

    一.异常 指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止. 异常体系 根类 java.lang.Throwable 两个直接子类 java.lang.Error 严重错误Er ...

  3. JavaScript基础学习之一

    目录 let和var之间的区别 作用域不同 变量提升 暂时性死区(temporal dead zone,简称 TDZ) 相同作用域下的重复声明 脚本调用 数据类型 Boolean Object 对象 ...

  4. FtpClient上传文件异常:java.net.SocketException: Connection reset

    FtpClient上传文件异常:java.net.SocketException: Connection reset 这问题折磨我快一天了,下午这会儿终于解决了,问题不在程序错误,原因还是出在上传图片 ...

  5. 并发QPS公式估算

    一.经典公式1: 一般来说,利用以下经验公式进行估算系统的平均并发用户数和峰值数据 1)平均并发用户数为 C = nL/T 2)并发用户数峰值 C' = C + 3*根号C C是平均并发用户数,n是l ...

  6. 重写org.springframework.beans.BeanUtils的copyProperties方法,能在实体映射的时候把纯数字格式的日期转格式

    就是在拷贝的时候加个正则的校验,如果是纯数字的日期 就转成yyyy-MM-dd HH:mm:ss的格式原本想直接用注解在实体转格式,但是那样实体会变成日期格式,所以放弃了,直接重写拷贝的方法比较简单 ...

  7. 9.Java的LinkedList/Deque相关方法

    Java的LinkedList/Deque中add/offer/push,remove/pop/poll的区别 它们来自不同的接口 add/remove源自集合,所以添加到队尾,从队头删除: offe ...

  8. python print 一个进度条

    import time scale=100 print("执行开始".center(scale+28,'-')) start = time.perf_counter() for i ...

  9. 解决df.to_csv 时增加重复双引号的问题

    df.to_csv("test.csv", sep='|',quoting=csv.QUOTE_NONE,index=False,header=True) 转载自 df.to_cs ...

  10. 函数XLOOKUP

    这个公式非OFFICE 365用户需要选中执行范围后 按Ctrl+Shift+Enter三键 (因为不支持公式溢出) XLOOKUP函数的基本结构是: =XLOOKUP(lookup_value,lo ...