设计模式系列之观察者模式(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方法绑定内置事件, ...
随机推荐
- Firemonkey 原生二维码扫描优化
之前用了ZXing的Delphi版本,运行自带的例子,速度非常慢,与安卓版本的相比查了很多,因此打算使用集成jar的方法,但是总觉得美中不足. 经过一番研究,基本上解决了问题. 主要有两方面的优化: ...
- Java进阶篇设计模式之八 ----- 责任链模式和命令模式
前言 在上一篇中我们学习了结构型模式的享元模式和代理模式.本篇则来学习下行为型模式的两个模式, 责任链模式(Chain of Responsibility Pattern)和命令模式(Command ...
- .net core 程序退出事件
平滑关闭,关闭事件 //捕获Ctrl+C事件 Console.CancelKeyPress += Console_CancelKeyPress; //进程退出事件 AppDomain.CurrentD ...
- 查看多核CPU各核的状态
1 top 命令,然后按数字“1” 2 命令:mpstat -P ALL 3 命令:sar -P ALL 输出较多,可grep或者重定向至文件查看 个人推荐使用第二种方式,操作方便且输出较少,看 ...
- 泛微关于js设计的一些小技巧
1.关于泛微流程的js设计 泛微oa可以插入javascript可以diy自己想要的表单页面前端功能.如果有前端开发经验,或者熟练使用jQuery的话,这将变得非常容易!同时泛微OA内部有很多库,包括 ...
- iOS----------四舍五入(只舍不入)
NSString * totalAssetString =@"1161000.00"; NSDecimalNumber *totalAssetNumber = [NSDecimal ...
- Android之崩溃日志管理
文章大纲 一.Android崩溃日志管理简介二.崩溃日志管理实战三.项目源码下载 一.Android崩溃日志管理简介 1. 什么是android崩溃日志管理 开发中有些地方未注意可能造成异常抛 ...
- mssql sqlserver 三种数据表数据去重方法分享
摘要: 下文将分享三种不同的数据去重方法数据去重:需根据某一字段来界定,当此字段出现大于一行记录时,我们就界定为此行数据存在重复. 数据去重方法1: 当表中最在最大流水号时候,我们可以通过关联的方式为 ...
- Windows Server 2016-启用默认Windows搜索服务
当我们使用Windows Server 2016尝试从开始菜单或基于Windows Server 2016的计算机上的Cortana 搜索时,我们不会收到任何结果或不一致的结果. 原因 默认情况下, ...
- Android JS桥交互("Uncaught ReferenceError: xxx is not defined or xxx has no method")
网上android和js交互的代码有不少,也很容易搜到.最近在做的项目需要用到js桥,遇到了一些问题,记录下来,希望以后遇到能马上解决掉. 一开始我找的demo是从这个:http://blog.csd ...