• 意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

  • 主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。

  • 何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

  • 如何解决:使用面向对象技术,可以将这种依赖关系弱化。

  • 关键代码:在抽象类里有一个 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模式,即ObservableObserver推送数据,后面我们自己实现的代码中使用了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)的更多相关文章

  1. 我理解设计模式C++实现观察者模式Observer Pattern

    概述: 近期中国股市起起伏伏,当然了起伏就用商机,小明发现商机后果断想入市,买入了中国证券,他想在电脑client上,网页上,手机上,iPad上都能够查看到该证券的实时行情,这样的情况下我们应该怎么设 ...

  2. 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)

    原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...

  3. 设计模式-观察者模式(Observer Pattern)

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

  4. 设计模式 - 观察者模式(Observer Pattern) 详细说明

    观察者模式(Observer Pattern) 详细说明 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...

  5. 设计模式 - 观察者模式(Observer Pattern) 详细解释

    观察者模式(Observer Pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部 ...

  6. 设计模式 - 观察者模式(Observer Pattern) Java内置 用法

    观察者模式(Observer Pattern) Java内置 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 ...

  7. 二十四种设计模式:观察者模式(Observer Pattern)

    观察者模式(Observer Pattern) 介绍定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新. 示例有一个Message实体类,某些对象 ...

  8. 设计模式 ( 十六 ) 观察者模式Observer(对象行为型)

    设计模式 ( 十六 ) 观察者模式Observer(对象行为型) 1.概述 一些面向对象的编程方式,提供了一种构建对象间复杂网络互连的能力.当对象们连接在一起时,它们就可以相互提供服务和信息. 通常来 ...

  9. jQuery中的观察者模式(Observer Pattern)

    在jQuery中,on方法可以为元素绑定事件,trigger方法可以手动触发事件,围绕这2个方法,我们来体验jQuery中的观察者模式(Observer Pattern). ■ on方法绑定内置事件, ...

随机推荐

  1. 卷积神经网络之AlexNet

    由于受到计算机性能的影响,虽然LeNet在图像分类中取得了较好的成绩,但是并没有引起很多的关注. 知道2012年,Alex等人提出的AlexNet网络在ImageNet大赛上以远超第二名的成绩夺冠,卷 ...

  2. 数据结构与算法(十):红黑树与TreeMap详细解析

    本文目录 一.为什么要创建红黑树这种数据结构 在上篇我们了解了AVL树,既然已经有了AVL这种平衡的二叉排序树,为什么还要有红黑树呢? AVL树通过定义我们知道要求树中每一个结点的左右子树高度差的绝对 ...

  3. 学习python的第一天

    2019.4.25自我总结 一.Typora 关于用Typora 自我感觉良好,基本快捷键也比较简单,ps:还是要多用用 二.编程 1.编程语言 是用来定义计算机程序的形式语言.它是一种被标准化的交流 ...

  4. Odoo开源智造IT经理人创业圆梦计划正式启动

    概念定义 IT经理人创业圆梦计划是什么? 甲方IT经理人的行业背景 + 其他甲方需求及可靠信任的线索资源 = 自主创业圆梦计划 具体措施 甲方IT经理人的职业行业背景取得其他甲方需求线索及信任——通过 ...

  5. #if和#ifdef的区别

    学习STM32偶然发现:在Keil中直接预先定义宏USE_STDPERIPH_DRIVER,但是却没有指定宏的值.而在头文件中判断用的是如下代码: #ifdef USE_STDPERIPH_DRIVE ...

  6. 解决WIN7启动DHCP服务报1075错误办法

    昨天电脑装了Microsoft .NET Framework后,今天一开电脑连不上网了,找了半天问题发现电脑的DHCP服务没有开启,然后在服务中开发DHCP提示下面的内容 原文地址:代码汇个人博客 h ...

  7. node项目自动化部署--基于Jenkins,Docker,Github(1)安装Jenkins

    前言 每次项目代码更新后都要重新部署,如果只有一台服务器还好. 但是如果是分布式系统,动不动就很多台服务器,所以代码的自动部署就显得十分重要了. 这里用几篇文章来记录一下如何使用Jenkins,Doc ...

  8. k8s实战之从私有仓库拉取镜像 - kubernetes

    1.实战目的 从私有docker仓库拉取镜像,部署pod.上一篇中,我们搭建了私有的镜像仓库,这一篇我们将与k8s结合实战使用私有仓库. 2.登录docker 为了完成本次实战,需要登录docker, ...

  9. 微信小程序去除Button默认样式

    在小程序开发过程中,使用率蛮高的组件button,因为经常要去除默认样式,然后再自定义样式,所以经常写,自己也总结分享一下简单的实现步骤. (一)实现效果1.实现前(默认样式): 2.实现后(去除默认 ...

  10. Spring的历史及哲学

    Spring的历史和哲学 1.Spring 历史 时间回到2002年,当时正是 Java EE 和 EJB 大行其道的时候,很多知名公司都是采用此技术方案进行项目开发.这时候有一个美国的小伙子认为 E ...