初步认识观察者模式

  观察者模式又称为发布/订阅(Publish/Subscribe)模式,因此我们可以用报纸期刊的订阅来形象的说明:

    报社方负责出版报纸.

    你订阅了该报社的报纸,那么只要报社发布了新报纸,就会通知你,或发到你手上.

    如果你不想再读报纸,可以取消订阅,这样,报社发布了新报纸就不会再通知你.

  理解其实以上的概念,就可以理解观察者模式,观察者模式中有主题(Subject)和观察者(Observer),分别对应报社和订阅用户(你).观察者模式定义了对象之间的一对多的依赖关系,这样,当"一"的一方状态发生变化时,它所依赖的"多"的一方都会收到通知并且自动更新.如图:

实现观察者模式

  这里以师生关系为例,老师和学生是一对多的关系,老师给学生布置作业,这个动作作为主题事件,每当老师布置一道题时,就要自动通知到所有的学生把该题记下来,然后再布置下一道题...

  这是一个并不标准的类图,主题接口和实现类中,一般需要有addObserver(),deleteObserver(),notifyObserver();用来注册删除观察者以及在主题状态发生变化时通知所有的观察者对象.

  接下来进行代码实现:

  Subject接口:

package com.wang.observer;
//主题接口
interface Subject {
//添加观察者
void addObserver(Observer obj);
//移除观察者
void deleteObserver(Observer obj);
//当主题方法改变时,这个方法被调用,通知所有的观察者
void notifyObserver();
}

  Oserver接口:

package com.wang.observer;

interface Observer {
//当主题状态改变时,会将一个String类型字符传入该方法的参数,每个观察者都需要实现该方法
public void update(String info);
}

Subject接口实现类TeacherSubject:

package com.wang.observer;

import java.util.ArrayList;
import java.util.List; public class TeacherSubject implements Subject {
//用来存放和记录观察者
private List<Observer> observers=new ArrayList<Observer>();
//记录状态的字符串
private String info; @Override
public void addObserver(Observer obj) {
observers.add(obj);
} @Override
public void deleteObserver(Observer obj) {
int i = observers.indexOf(obj);
if(i>=0){
observers.remove(obj);
}
} @Override
public void notifyObserver() {
for(int i=0;i<observers.size();i++){
Observer o=(Observer)observers.get(i);
o.update(info);
}
}
//布置作业的方法,在方法最后,需要调用notifyObserver()方法,通知所有观察者更新状态
public void setHomework(String info){
this.info=info;
System.out.println("今天的作业是"+info);
this.notifyObserver();
} }

Observer接口实现类StudentObserver:

package com.wang.observer;

public class StudentObserver implements Observer {

    //保存一个Subject的引用,以后如果可以想取消订阅,有了这个引用会比较方便
private TeacherSubject t;
//学生的姓名,用来标识不同的学生对象
private String name;
//构造器用来注册观察者
public Student(String name,Teacher t) {
this.name=name;
this.t = t;
//每新建一个学生对象,默认添加到观察者的行列
t.addObserver(this);
} @Override
public void update(String info) {
System.out.println(name+"得到作业:"+info); } }

测试类TestObserver:

package com.wang.observer;

public class TestObserver {

    public static void main(String[] args) {

        TeacherSubject teacher=new TeacherSubject();
StudentObserver zhangSan=new StudentObserver("张三", teacher);
StudentObserver LiSi=new StudentObserver("李四", teacher);
StudentObserver WangWu=new StudentObserver("王五", teacher); teacher.setHomework("第二页第六题");
teacher.setHomework("第三页第七题");
teacher.setHomework("第五页第八题");
}
}

打印结果:

今天的作业是第二页第六题
张三得到作业:第二页第六题
李四得到作业:第二页第六题
王五得到作业:第二页第六题
今天的作业是第三页第七题
张三得到作业:第三页第七题
李四得到作业:第三页第七题
王五得到作业:第三页第七题
今天的作业是第五页第八题
张三得到作业:第五页第八题
李四得到作业:第五页第八题
王五得到作业:第五页第八题

从打印结果看,每当老师布置作业的状态改变,就会通知每一个学生.以上就是一个简单的观察者模式的实现.

java内置的观察者模式:

  在java.util包中包含有基本的Observer接口和Observable抽象类.功能上和Subject接口和Observer接口类似.不过在使用上,就方便多了,因为许多功能比如说注册,删除,通知观察者的那些功能已经内置好了.

  使用javaAPI的观察者模式需要明白这么几件事情:

   如何使对象变为观察者?

    实现观察者接口(java.util.Observer),然后调用Observable对象的addObserver()方法.不想再当观察者时,调用deleteObserver()就可以了.

   被观察者(主题)如何发出通知?

    第一步:先调用setChanged()方法,标识状态已经改变的事实.

    第二步:调用notifyObservers()方法或者notifyObservers(Object arg),这就牵扯到推(push)和拉(pull)的方式传送数据.如果想用push的方式"推"数据给观察者,可以把数据当做数据对象传送给notifyObservers(Object arg)方法,其中的arg可以为任意对象,意思是你可以将任意对象传送给每一个观察者.如果调用不带参数的notifyObserver()方法,则意味着你要使用pull的方式去主题对象中"拉"来所需要的数据.

   观察者如何接收通知?

     观察者只需要实现一个update(Observable o,Object arg)方法,第一个参数o,是指定通知是由哪个主题下达的,第二个参数arg就是上面notifyObserver(Object arg)里传入的数据,如果不传该值,arg为null.

  下面使用java内置API实现上面我所写的老师和学生的例子:

被观察者TeacherSubject:

package com.wang.observer1;

import java.util.Observable;

public class Teacher extends Observable {
//布置作业的状态信息字符串
private String info;
public void setHomework(String info) { this.info=info;
System.out.println("布置的作业是"+info); setChanged();
notifyObservers();
}
public String getInfo() {
return info;
}
}

观察者StudentObserver:

package com.wang.observer1;

import java.util.Observable;
import java.util.Observer; public class Student implements Observer{ private Observable ob;
private String name; public Student(String name,Observable ob) {
this.ob = ob;
this.name=name;
ob.addObserver(this);
} @Override
public void update(Observable o, Object arg) {
Teacher t=(Teacher)o;
System.out.println(name+"得到作业信息:"+t.getInfo()); } }

  测试代码和打印结果我就不再写了,和上面的例子是一样一样的,在这个例子中我使用的是"pull"的方式拉数据,在需要传递状态的TeacherSubject中定义了一个info字符串的get方法,在观察者对象中调用get方法得到所需数据,如果希望使用push的方式,只需要在TeacherSubject类的notifyOservers()方法中传入String类型的info字符串即可在update()方法中直接通过第二个参数获取到arg,即使前面传过来的info字符串.

观察者模式的好处

  观察者模式提供了一种对象设计,让主题和观察者之间耦合度降得很低,为什么呢?关于观察者的一切,主题只知道观察者实现了Observer接口,并不需要观察者具体的类是谁,做了什么或者其他细节.

  这样的话,由于松耦合,改变主题或者观察者其中一方,并不会影响另一方,只要他们之间的接口仍被遵守,就可以自由地改变它.

  降低对象之间的耦合度,也是面设对象设计的一个很重要的原则.  

设计模式--观察者模式初探和java Observable模式的更多相关文章

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

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

  2. Java Observable 模式

    一.Observer模式的意图: 在对象的内部状态发生变化时,自动通知外部对象进行响应. 二.Observer模式的构成: ·被观察者:内部状态有可能被改变,而且又需要通知外部的对象 ·观察者:需要对 ...

  3. 大型Java进阶专题(八)设计模式之适配器模式、装饰者模式和观察者模式

    前言 ​ 今天开始我们专题的第八课了.本章节将介绍:三个设计模式,适配器模式.装饰者模式和观察者模式.通过学习适配器模式,可以优雅的解决代码功能的兼容问题.另外有重构需求的人群一定需要掌握装饰者模式. ...

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

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

  5. <代码整洁之道>、<java与模式>、<head first设计模式>读书笔记集合

    一.前言                                                                                       几个月前的看书笔记 ...

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

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

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

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

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

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

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

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

随机推荐

  1. 页面嵌入dom与被嵌入iframe的攻防

    1.情景一:自己的页面被引入(嵌入)至别人的页面iframe中 if(window.self != window.top){ //url是自己页面的url window.top.location.hr ...

  2. dotNET跨平台相关文档整理

    一直在从事C#开发的相关技术工作,从C# 1.0一路用到现在的C# 6.0, 通常情况下被局限于Windows平台,Mono项目把我们C#程序带到了Windows之外的平台,在工作之余花了很多时间在M ...

  3. Android学习探索之Java 8 在Android 开发中的应用

    前言: Java 8推出已经将近2年多了,引入很多革命性变化,加入了函数式编程的特征,使基于行为的编程成为可能,同时减化了各种设计模式的实现方式,是Java有史以来最重要的更新.但是Android上, ...

  4. MySQL中interactive_timeout和wait_timeout的区别

    在用mysql客户端对数据库进行操作时,打开终端窗口,如果一段时间没有操作,再次操作时,常常会报如下错误: ERROR (HY000): Lost connection to MySQL server ...

  5. Java消息队列--JMS概述

    1.什么是JMS JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送 ...

  6. ASP.Net MVC——使用 ITextSharp 完美解决HTML转PDF(中文也可以)

    前言: 最近在做老师交代的一个在线写实验报告的小项目中,有这么个需求:把学生提交的实验报告(HTML形式)直接转成PDF,方便下载和打印. 以前都是直接用rdlc报表实现的,可这次牵扯到图片,并且更为 ...

  7. 编译器开发系列--Ocelot语言6.静态类型检查

    关于"静态类型检查",想必使用C 或Java 的各位应该非常熟悉了.在此过程中将检查表达式的类型,发现类型不正确的操作时就会报错.例如结构体之间无法用+ 进行加法运算,指针和数值之 ...

  8. 解决mysql插入数据时出现Incorrect string value: '\xF0\x9F...' for column 'name' at row 1的异常

    这个问题,原因是UTF-8编码有可能是两个.三个.四个字节.Emoji表情或者某些特殊字符是4个字节,而MySQL的utf8编码最多3个字节,所以数据插不进去. 我的解决方案是这样的 1.在mysql ...

  9. HTML5 标签 details 展开 搜索

    details有一个新增加的子标签--summary,当鼠标点击summary标签中的内容文字时,details标签中的其他所有元素将会展开或收缩. 默认状态为 收缩状态 设置为展开状态为 <d ...

  10. Linux实战教学笔记02:计算机系统硬件核心知识

    标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 互联网企业常见服务器介绍 1.1 互联网公司服务器品牌 - DELL(大多数公司,常用) - HP - IBM(百度在用) 浪潮 联想 航天联 ...