设计模式系列之观察者模式(Observer Pattern)
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
JDK中的API实现观察者模式
JDK自己实现了一系列的观察者API,我们可以利用他来实现我们的设计模式,后面我们自己模仿之实现这些操作.
主要是一个接口ObServe和一个类Obeservable,可观察者继承Observable ,观察者实现ObServe,然后观察者持有Observable来注册自己.
那么我们现在的需求是,软件开发人员向猎头注册自己,猎头有招聘消息的时候会通知在这里注册的软件开发人员,下面我们先实现猎头的代码
实现可观察者Observable(猎头)
package com.zhoutao123.design.pattern.observer.java;
import java.util.Observable;
/**
* 猎头
*/
public class Headunting extends Observable {
private String invite = "请于明天到九华北路参加面试";
public void setTime(String invite) {
this.invite = invite;
// 这句话非常重要,设置数据已经被改变
setChanged();
System.out.println("--------------------猎头发布消息--------------");
// 推消息
notifyObservers(invite);
}
}
实现观察者Observer(软件开发人员)
观察者持有可被观察者对象,在构造方法中实现了向被观察者注册自己
package com.zhoutao123.design.pattern.observer.java;
import java.util.Observable;
import java.util.Observer;
/**
* 前端开发
*/
public class ApplicantWithFe implements Observer {
// 持有Observable 方便取消注册
private Observable observable;
public ApplicantWithFe(Observable observable) {
this.observable = observable;
// 向被观察者注册自己
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if(arg instanceof String){
System.out.println("我是前端开发工程师,我已经接受到面试邀请---->" + (String)arg );
}
}
// 取消注册自己
public void unregrester(){
System.out.println("我是前端工程师,目前已经就业,取消注册");
observable.deleteObserver(this);
}
}
package com.zhoutao123.design.pattern.observer.java;
import java.util.Observable;
import java.util.Observer;
/**
* Go语言开发工程师
*/
public class ApplicantWithGo implements Observer {
// 持有Observable 方便取消注册
private Observable observable;
public ApplicantWithGo(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if(arg instanceof String){
System.out.println("我是Go开发工程师,我已经接受到面试邀请---->" + (String)arg );
}
}
public void unregrester(){
System.out.println("我是GO工程师,目前已经就业,取消注册");
observable.deleteObserver(this);
}
}
package com.zhoutao123.design.pattern.observer.java;
import java.util.Observable;
import java.util.Observer;
/**
* Java 开发工程师
*/
public class ApplicantWithJava implements Observer {
// 持有Observable 方便取消注册
private Observable observable;
public ApplicantWithJava(Observable observable) {
this.observable = observable;
observable.addObserver(this);
}
@Override
public void update(Observable o, Object arg) {
if(arg instanceof String){
System.out.println("我是Java开发工程师,我已经接受到面试邀请---->" + (String)arg );
}
}
public void unregrester(){
System.out.println("=============> 我是Java工程师,目前已经就业,取消注册");
observable.deleteObserver(this);
}
}
目前我们使用是push模式,即Observable向Observer推送数据,后面我们自己实现的代码中使用了pull模式,即Observable通知Observer数据已经改变,然后Observer通过Observable获取数据.
测试代码
创建Observable(Headunting)和若干个Observer(ApplicantWithJava/Go/Fe)
然后Observable(Headunting) 设置消息,观察者就会接到数据,输出效果.
package com.zhoutao123.design.pattern.observer.java;
public class Test {
public static void main(String[] args) {
// 创建猎头
Headunting headunting = new Headunting();
// 创建若干应聘者
ApplicantWithJava applicantWithJava = new ApplicantWithJava(headunting);
ApplicantWithFe applicantWithFe = new ApplicantWithFe(headunting);
ApplicantWithGo applicantWithGo = new ApplicantWithGo(headunting);
// 猎头发布招聘消息
headunting.setTime("后天10:00 到安徽合肥九华北路171号参加面试");
//Java 工程师找到了工作退出招聘
applicantWithJava.unregrester();
headunting.setTime("大后天15:00 到江苏南京九华北路181号参加面试");
}
}
测试输出
可以看到,猎头发布消息后,已经注册的用户会接受到消息,然后Java工程师推出注册,然后猎头再次发送消息后,只有两个人接受到消息.
--------------------猎头发布消息--------------
我是Go开发工程师,我已经接受到面试邀请---->后天10:00 到安徽合肥九华北路171号参加面试
我是前端开发工程师,我已经接受到面试邀请---->后天10:00 到安徽合肥九华北路171号参加面试
我是Java开发工程师,我已经接受到面试邀请---->后天10:00 到安徽合肥九华北路171号参加面试
=============> 我是Java工程师,目前已经就业,取消注册
--------------------猎头发布消息--------------
我是Go开发工程师,我已经接受到面试邀请---->大后天15:00 到江苏南京九华北路181号参加面试
我是前端开发工程师,我已经接受到面试邀请---->大后天15:00 到江苏南京九华北路181号参加面试
自定义实现观察者模式
自定义实现观察者模式的代码中,我们首先实现两个基本的接口,和上面的实现方法类型类似.这里说明一下我们的需求,老师作为可观察者,学生做为观察者,当老师发布数据的时候,学生根据自己的实现来实现不同的计算功能.
下面我们开始从头开始实现代码:
可观察者接口
package com.zhoutao123.design.pattern.observer.self;
public interface SelfObservable {
/**
* 获取数据
*
* @return
*/
NumberData getData();
/**
* 通知所有Observer
* 此处需过需要不同的过滤模式,可是使用之前学过的过滤器模式实现
*/
void notifyObservers();
/**
* 增加观察者
* @param observer
*/
void addObserver(SelfObserver observer);
/**
* 移除观察者
* @param observer
*/
void removeObserver(SelfObserver observer);
}
观察者接口
package com.zhoutao123.design.pattern.observer.self;
public interface SelfObserver {
/**
* 更新数据,这里和JDK实现的不同,是PULL,拉数据
*/
void update();
/**
* 取消注册
*/
void unregister();
}
具体实现
首先实现数据的模型,很简单的JavaBean对象
package com.zhoutao123.design.pattern.observer.self;
public class NumberData {
private Integer num1;
private Integer num2;
public NumberData(Integer num1, Integer num2) {
this.num1 = num1;
this.num2 = num2;
}
// 省略Setter/Getter方式
}
首先是老师,老师实现了可观察者接口
package com.zhoutao123.design.pattern.observer.self;
import java.util.ArrayList;
import java.util.List;
public class Teacher implements SelfObservable {
// 注册的学生集合
List<SelfObserver> observers;
// 持有的数据
NumberData numberData;
// 初始化自己持有的数据
public Teacher() {
this.observers = new ArrayList<>();
numberData = new NumberData(0,0);
}
// 获取当前数据
@Override
public NumberData getData() {
return numberData;
}
// 更新数据,注意更新数据的时候调用notifyObservers用于通知所有的观察者
// 如果需要多种过滤方法,可以尝试之前学习的过滤器模式
public void setNumberData(NumberData numberData) {
this.numberData = numberData;
System.out.println("----------------老师发布了新数据----------------");
notifyObservers();
}
// 遍历,调用每个观察者的update方法
@Override
public void notifyObservers() {
for (SelfObserver observer : observers) {
observer.update();
}
}
// 新增观察者
@Override
public void addObserver(SelfObserver selfObserver) {
observers.add(selfObserver);
}
// 取消观察者
@Override
public void removeObserver(SelfObserver selfObserver) {
observers.remove(selfObserver);
}
}
学生的实现代码,学生写了3个学生,在接受到数据之后,分别会计算,和差以及乘积,然后输出.
package com.zhoutao123.design.pattern.observer.self;
public class StudentA implements SelfObserver {
SelfObservable selfObservable;
// 初始化的时候,开始持有可观察者
public StudentA(SelfObservable selfObservable) {
this.selfObservable = selfObservable;
selfObservable.addObserver(this);
}
@Override
public void update() {
// 这里是拉数据,从可观察者哪里获取数据,而不是可观察者在调用update方法时候push过来的
NumberData data = selfObservable.getData();
System.out.println(String.format("接受到老师发过来的数据:%d ,%d 计算他们的和是:%d",data.getNum1(),data.getNum2(),data.getNum1() + data.getNum2()));
}
// 取消自己的注册
@Override
public void unregister() {
selfObservable.removeObserver(this);
}
}
package com.zhoutao123.design.pattern.observer.self;
public class StudentB implements SelfObserver {
SelfObservable selfObservable;
public StudentB(SelfObservable selfObservable) {
this.selfObservable = selfObservable;
selfObservable.addObserver(this);
}
@Override
public void update() {
NumberData data = selfObservable.getData();
System.out.println(String.format("接受到老师发过来的数据:%d ,%d 计算他们的差是:%d",data.getNum1(),data.getNum2(),data.getNum1() - data.getNum2()));
}
@Override
public void unregister() {
selfObservable.removeObserver(this);
}
}
package com.zhoutao123.design.pattern.observer.self;
public class StudentC implements SelfObserver {
SelfObservable selfObservable;
public StudentC(SelfObservable selfObservable) {
this.selfObservable = selfObservable;
selfObservable.addObserver(this);
}
@Override
public void update() {
NumberData data = selfObservable.getData();
System.out.println(String.format("接受到老师发过来的数据:%d ,%d 计算他们的乘积是:%d",data.getNum1(),data.getNum2(),data.getNum1() * data.getNum2()));
}
@Override
public void unregister() {
selfObservable.removeObserver(this);
}
}
测试代码
package com.zhoutao123.design.pattern.observer.self;
public class Test {
public static void main(String[] args) {
// 创建老师
Teacher teacher = new Teacher();
// 创建学生
StudentA a = new StudentA(teacher);
StudentB b = new StudentB(teacher);
StudentC c = new StudentC(teacher);
//老师发布数据
teacher.setNumberData(new NumberData(12,6));
// 一名学生走了
c.unregister();
teacher.setNumberData(new NumberData(2,6));
// 又走一个学生
a.unregister();
teacher.setNumberData(new NumberData(1,3));
}
}
测试效果
这里也就很简单了,自己尝试这分析一下
----------------老师发布了新数据----------------
接受到老师发过来的数据:12 ,6 计算他们的和是:18
接受到老师发过来的数据:12 ,6 计算他们的差是:6
接受到老师发过来的数据:12 ,6 计算他们的乘积是:72
----------------老师发布了新数据----------------
接受到老师发过来的数据:2 ,6 计算他们的和是:8
接受到老师发过来的数据:2 ,6 计算他们的差是:-4
----------------老师发布了新数据----------------
接受到老师发过来的数据:1 ,3 计算他们的差是:-2
总结
通过这两个例子,详细自己对观察者模式有了更深的理解,自己总结了一些步骤,如下:
- 创建可观察者
- 创建观察者
- 向可观察者中注册观察者(这一步可以在创建观察者的时候执行)
- 可观察更新数据,使用notifyObserver()通知所有的观察者
- 观察者接受到请求,获取数据(pull/push),然后分别执行自己的逻辑
设计模式系列之观察者模式(Observer Pattern)的更多相关文章
- 我理解设计模式C++实现观察者模式Observer Pattern
概述: 近期中国股市起起伏伏,当然了起伏就用商机,小明发现商机后果断想入市,买入了中国证券,他想在电脑client上,网页上,手机上,iPad上都能够查看到该证券的实时行情,这样的情况下我们应该怎么设 ...
- 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)
原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...
- 设计模式-观察者模式(Observer Pattern)
观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己. 观察者 ...
- 设计模式 - 观察者模式(Observer Pattern) 详细说明
观察者模式(Observer Pattern) 详细说明 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...
- 设计模式 - 观察者模式(Observer Pattern) 详细解释
观察者模式(Observer Pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...
- 设计模式 - 观察者模式(Observer Pattern) Java内置 用法
观察者模式(Observer Pattern) Java内置 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 ...
- 二十四种设计模式:观察者模式(Observer Pattern)
观察者模式(Observer Pattern) 介绍定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新. 示例有一个Message实体类,某些对象 ...
- 设计模式 ( 十六 ) 观察者模式Observer(对象行为型)
设计模式 ( 十六 ) 观察者模式Observer(对象行为型) 1.概述 一些面向对象的编程方式,提供了一种构建对象间复杂网络互连的能力.当对象们连接在一起时,它们就可以相互提供服务和信息. 通常来 ...
- jQuery中的观察者模式(Observer Pattern)
在jQuery中,on方法可以为元素绑定事件,trigger方法可以手动触发事件,围绕这2个方法,我们来体验jQuery中的观察者模式(Observer Pattern). ■ on方法绑定内置事件, ...
随机推荐
- <转载>Android性能优化之HashMap,ArrayMap和SparseArray
本篇博客来自于转载,打开原文地址已经失效,在此就不贴出原文地址了,如原作者看到请私信我可用地址,保护原创,人人有责. Android开发者都知道Lint在我们使用HashMap的时候会给出警告—— ...
- Python爬虫入门教程 37-100 云沃客项目外包网数据爬虫 scrapy
爬前叨叨 2019年开始了,今年计划写一整年的博客呢~,第一篇博客写一下 一个外包网站的爬虫,万一你从这个外包网站弄点外快呢,呵呵哒 数据分析 官方网址为 https://www.clouderwor ...
- <<C语言--神奇的指针>>
指针很简单 ------引子 学计算机语言,首先推荐C语言.无论是数据结构还是算法,站在C语言的角度,会让我们理解的更加清晰透彻. 但是,指针不太"友好",让很多人抓狂,头疼.不少 ...
- 『vue踩坑日常』 在index.html中引入静态文件不生效
Vue日常踩坑日常 -- 在index.html中引入静态文件不生效问题 本文针对的是Vue小白,不喜勿喷,谢谢 出现该问题的标志如下 控制台warning(Resource interpreted ...
- JVM内存结构/JVM运行时数据区,以及堆内存的划分
1.程序计数器: 程序计数器是线程私有的内存,JVM多线程是通过线程轮流切换并分配处理器执行时间的方式实现的,当线程切换后需要恢复到正确的执 行位置(处理器)时,就是通过程序计数器来实现的.此内存区域 ...
- libaio.so.1()(64bit) is needed by MySQL-server 问题解决办法
[root@localhost upload]# rpm -ivh MySQL-server-5.5.25a-1.rhel5.x86_64.rpmerror: Failed dependencies: ...
- 从壹开始微服务 [ DDD ] 之八 ║剪不断理还乱的 值对象和Dto
缘起 哈喽大家周四好,时间是过的真快,这几天一直忙着在公司的项目,然后带带新人,眼看这周要过去了,还是要抽出时间学习学习,这些天看到群里的小伙伴也都在忙着新学习,还是很开心的,至少当时的初衷已经达到了 ...
- request.getContextPath()
今天终于明白了jsp中的request.getContextPath()是怎么回事了. request.getContextPath() 返回站点的根目录 request.getRealpath(& ...
- 网络协议 21 - RPC 协议(中)- 基于 JSON 的 RESTful 接口协议
上一节我们了解了基于 XML 的 SOAP 协议,SOAP 的 S 是啥意思来着?是 Simple,但是好像一点儿都不简单啊! 传输协议问题 对于 SOAP 来讲,比如我创建一个订单, ...
- NavigationViewDemo【和DrawerLayout搭配使用实现侧滑导航视图界面】
版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 主要记录NavigationView的使用,而一般情况下NavigationView是和DrawerLayout搭配使用的,还有To ...