本文源码:GitHub·点这里 || GitEE·点这里

一、观察者模式

1、概念描述

观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式。观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,主题对象在状态发生变化时,会通知所有观察者对象。Redis和常用消息中间件的发布订阅模式,都是基于该原理实现。

2、核心角色

  • 抽象主题角色

抽象主题角色把所有对观察者对象的统一聚集管理,每个主题都可以有一个或多个观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。

  • 具体主题角色

将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色。

  • 抽象观察者角色

为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。

  • 具体观察者角色

具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态协调同步。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

3、源代码实现

  • 基于消息推送模式

主题对象向观察者推送主题的消息,不管观察者是否需要。

/**
* 观察者设计模式
*/
public class C01_Observer {
public static void main(String[] args) {
// 创建主题对象
ConcreteSubject subject = new ConcreteSubject();
// 创建观察者对象
Observer observer1 = new ConcreteObserver("观察者A");
Observer observer2 = new ConcreteObserver("观察者B");
// 注册观察者
subject.attach(observer1);
subject.attach(observer2);
// 修改主题状态
subject.change("New State !");
/**
* 主题状态:New State !
*【观察者A】状态:New State !
*【观察者B】状态:New State !
*/
}
}
// 抽象主题角色
abstract class Subject {
// 保存注册的观察者对象
private List<Observer> list = new ArrayList<>();
/**
* 注册观察者对象
*/
public void attach (Observer observer){
list.add(observer);
System.out.println("注册一个观察者:"+observer.getClass().getName());
}
/**
* 删除观察者对象
*/
public void delete (Observer observer){
list.remove(observer);
System.out.println("删除一个观察者:"+observer);
}
/**
* 通知所有注册的观察者对象
*/
public void notifyObserver (String newState){
for (Observer observer : list) {
observer.update(newState);
}
}
}
// 具体主题角色
class ConcreteSubject extends Subject{
private String state ;
public String getState (){
return state ;
}
public void change (String newState){
state = newState;
System.out.println("主题状态:"+state);
//状态发生改变,通知各个观察者
this.notifyObserver(state);
}
}
// 抽象观察者角色
interface Observer {
/**
* 更新接口
*/
void update (String state);
}
// 具体观察者角色
class ConcreteObserver implements Observer{
private String name ;
// 观察者状态
private String observerState ;
public ConcreteObserver (String name){
this.name = name ;
}
/**
* 更新观察者的状态,使其与目标的状态保持一致
*/
@Override
public void update(String state) {
observerState = state ;
System.out.println("【"+this.name+"】状态:"+observerState);
}
}
  • 基于消息拉取模式

主题对象在通知观察者的时候,传递少量信息。如果观察者需要该消息内容,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。

该案例基于上述案例修改,观察者获取主题对象的消息话题,只有自己感兴趣的话题,才进一步获取内容。

public class C02_Observer_Pull {
public static void main(String[] args) {
// 创建主题对象
ConcreteSubject1 subject = new ConcreteSubject1();
// 创建观察者对象
Observer1 observer1 = new ConcreteObserver1("观察者A","JAVA");
Observer1 observer2 = new ConcreteObserver1("观察者B","MySQL");
// 注册观察者
subject.attach(observer1);
subject.attach(observer2);
/*
* 修改主题状态
* 主题状态:JAVA State !
* 【观察者A】状态:JAVA State !
* 主题状态:MySQL State !
* 【观察者B】状态:MySQL State !
*/
subject.change("JAVA State !","JAVA");
subject.change("MySQL State !","MySQL");
}
}
abstract class Subject1 {
// 保存注册的观察者对象
private List<Observer1> list = new ArrayList<>();
/**
* 注册观察者对象
*/
public void attach (Observer1 observer){
list.add(observer);
}
/**
* 删除观察者对象
*/
public void delete (Observer1 observer){
list.remove(observer);
System.out.println("删除一个观察者:"+observer);
}
/**
* 通知所有注册的观察者对象,传入消息的话题
*/
public void notifyObservers (String msgTopic){
for (Observer1 observer : list){
observer.update(this);
}
}
}
class ConcreteSubject1 extends Subject1 {
private String state ;
private String msgTopic ;
public String getState (){
return state ;
}
public String getMsgTopic (){
return msgTopic ;
}
public void change (String newState,String newMsgTopic){
this.state = newState ;
this.msgTopic = newMsgTopic ;
System.out.println("主题状态:"+state);
this.notifyObservers(msgTopic);
}
} interface Observer1 {
/**
* 更新接口
* @param subject 传入主题对象,方面获取相应的主题对象的状态
*/
void update(Subject1 subject);
}
class ConcreteObserver1 implements Observer1{
private String name ;
// 选择话题
private String msgTopic ;
// 观察者状态
private String observerState ;
public ConcreteObserver1 (String name,String msgTopic){
this.name = name ;
this.msgTopic = msgTopic ;
}
@Override
public void update(Subject1 subject) {
ConcreteSubject1 concreteSubject1 = (ConcreteSubject1)subject ;
// 只有指定话题才拉取消息
if (concreteSubject1.getMsgTopic().equals(msgTopic)){
observerState = concreteSubject1.getState();
System.out.println("【"+this.name+"】状态:"+observerState);
}
}
}

4、两种模式比较

推模式是假定主题对象知道观察者需要的数据,直接推送,使得观察者对象难以复用;而拉模式是主题对象不知道观察者具体需要什么数据,将把自身传递给观察者,按需要取值。

二、JDK中应用

JAVA语言的java.utill类库里面,提供了一个Observable类以及一个Observer接口,构成JAVA语言对观察者模式的支持。

1、Observer接口

这个接口只定义了一个方法,即update()方法,当被观察者对象的状态发生变化时,被观察者对象的notifyObservers()方法就会调用这一方法。

package java.util;
/**
* A class can implement the <code>Observer</code> interface when it
* wants to be informed of changes in observable objects.
*/
public interface Observer {
/**
* This method is called whenever the observed object is changed. An
* application calls an <tt>Observable</tt> object's
*/
void update(Observable o, Object arg);
}

2、Observable类

被观察者类都是java.util.Observable类的子类。java.util.Observable提供方法支持观察者对象。

  • setChanged方法:观察者对象的状态发生了变化。
  • notifyObservers:调用所有登记过的观察者对象的update()方法。
package java.util;
public class Observable {
private boolean changed = false;
private Vector obs;
/** Construct an Observable with zero Observers. */
public Observable() {
obs = new Vector();
}
/**将一个观察者添加到观察者聚集上面*/
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
/** 将一个观察者从观察者聚集上删除 */
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
public void notifyObservers() {
notifyObservers(null);
}
/**
* 如果本对象有变化(那时hasChanged 方法会返回true)
* 调用本方法通知所有登记的观察者,即调用它们的update()方法
* 传入this和arg作为参数
*/
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
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();
}
/** 将“已变化”设置为true */
protected synchronized void setChanged() {
changed = true;
}
/** 将“已变化”重置为false */
protected synchronized void clearChanged() {
changed = false;
}
public synchronized boolean hasChanged() {
return changed;
}
public synchronized int countObservers() {
return obs.size();
}
}

3、应用案例

public class C03_Observer_JDK {
public static void main(String[] args) {
//创建被观察者对象
MsgSource msgSource = new MsgSource();
//创建观察者对象,并将被观察者对象登记
MsgConsumer watcher = new MsgConsumer(msgSource);
msgSource.setData("Hello,Java");
msgSource.setData("Bye Java");
}
}
class MsgSource extends Observable {
private String data = "";
public String getData() {
return data;
}
public void setData(String data) {
if(!this.data.equals(data)){
this.data = data;
setChanged();
}
notifyObservers();
}
}
class MsgConsumer implements java.util.Observer {
// 添加观察者
public MsgConsumer(Observable msgSource){
msgSource.addObserver(this);
}
// 状态获取
@Override
public void update(Observable o, Object arg) {
System.out.println("消息内容:" + ((MsgSource)o).getData());
}
}

三、优缺点总结

观察者模式的主要的作用是对象解耦,将观察者和被观察者隔离。

程序中包括多个被观察者和多个被观察者,开发和调试比较复杂,而且Java中的消息的通知默认是顺序执行的,一个观察者的执行阻塞会影响整体的执行效率。

四、源代码地址

GitHub·地址
https://github.com/cicadasmile/model-arithmetic-parent
GitEE·地址
https://gitee.com/cicadasmile/model-arithmetic-parent

Java描述设计模式(11):观察者模式的更多相关文章

  1. JAVA的设计模式之观察者模式----结合ActiveMQ消息队列说明

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

  2. Java常见设计模式之观察者模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述观察者(Observer)模式的: 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Mo ...

  3. JAVA基础——设计模式之观察者模式

    观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式.模型-视图(Model/View)模式.源-监听器(Source/Listener)模式或从属者(Dependen ...

  4. java小白设计模式之观察者模式

    观察者模式: 对象之间多对一依赖的一种设计方案,被依赖对象为Subject(一),依赖对象为Observer(多),Subject通知Observer变化直接代码: package com.wz.tw ...

  5. Java描述设计模式(15):责任链模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景描述 1.请假审批流程 公司常见的请假审批流程:请假天数 当 day<=3 天,项目经理审批 当 3<day<= ...

  6. Java描述设计模式(24):备忘录模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 常见的视频播放软件都具备这样一个功能:假设在播放视频西游记,如果这时候切换播放视频红楼梦,当再次切回播放西游记时, ...

  7. Java描述设计模式(12):外观模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 在移动互联网没有普及之前,去饭店吃饭的流程大致如下:选座位,排队,点菜,结账.后来移动互联网普及,通过手机APP就 ...

  8. Java描述设计模式(09):装饰模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.生活场景 1.场景描述 孙悟空有七十二般变化,他的每一种变化都给他带来一种附加的本领.他变成鱼儿时,就可以到水里游泳:他变成鸟儿时,就可以 ...

  9. Java描述设计模式(08):桥接模式

    本文源码:GitHub·点这里 || GitEE·点这里 一.桥接模式简介 1.基础描述 桥梁模式是对象的结构模式.又称为柄体(Handle and Body)模式或接口(Interface)模式.桥 ...

随机推荐

  1. ansible部署nginx

    1.配置免密登录 [root@localhost ansible]# vim /etc/ansible/hosts //用来配置目标主机 加入以下内容 [nginx] //目标主机组 192.168. ...

  2. webpack学习_使用source map

    追踪错误和警告,JS提供sourcemap功能,将编译后的代码映射回原始代码(简单来说就是即使打包后,也可以检测知道该错误来自哪个JS文件).如果一个错误来自与b.js,那么source map回明确 ...

  3. CCF-CSP题解 201509-3 模板生成系统

    简单的替换一下字符串. 注意数组开大点. #include<bits/stdc++.h> const int maxm = 100; const int maxn = 100; using ...

  4. 解决苹果mac远程桌面无VDI客户端

    解决苹果mac远程桌面云aDesk无VDI客户端 因集团办公工作需要使用桌面云aDesk 在深信服官网并未有mac 的VDI Client客户端 mac电脑可通过Google浏览器访问VDI的服务器地 ...

  5. 从《彩色圆环》一题探讨一类环上dp的解法

    清橙A1202 bzoj2201 bsoj4074 试题来源 2010中国国家集训队命题答辩 问题描述 小A喜欢收集宝物.一天他得到了一个圆环,圆环上有N颗彩色宝石,闪闪发光.小A很爱惜这个圆环,天天 ...

  6. Dynamics 365中的Client API form context (formContext)

    适用于Dynamics 365 for Customer Engagement apps 9.x版本. 本文是一篇翻译,原文来源是微软官方文档. 本文链接:https://www.cnblogs.co ...

  7. PuppeteerSharp读取页面完整HTML(.NetCore)

    1.使用NUGET安装PuppeteerSharp 通过工具或者命令方式安装 2.初始化浏览器 await new BrowserFetcher().DownloadAsync(BrowserFetc ...

  8. (办公)记事本_Linux帮助命令

    参考:http://www.gulixueyuan.com/course/300/task/7086/show# 帮助命令: .man命令 1.1.man命令是Linux下的帮助指令,通过man指令可 ...

  9. Linux 周期任务

    一次性任务 在某个特定的时间,执行一次后被清除 相关命令/进程 at 命令 atd进程 在centos6中,系统服务的名称: /etc/init.d/atd 查看系统上该进程时候启动: [root@e ...

  10. What happened when new an object in JVM ?

    原文链接:https://www.javaspring.net/java/what-happened-when-new-an-object-in-jvm I. Introduction As you ...