观察者模式:被观察的目标对象的内容发生变化时,通知注册到目标对象上所有的观察者,内容发生了变化,观察者对象根据变化进行相关的操作;适用于多个对象(一个对象也可以)依赖于另外一个对象的状态的变化,动态地改变自身的内容的情况。Java本身设计了观察模式的实现,预留出了接口和类,先说观察者模式实现的原理,后面在贴上用java本身实现观察这模式,下面正式开始思路和代码部分;
被观察的对象,既目标对象(Subject)在本身维护一个包含所有观察者的集合,当目标对象的内容发生变化时,遍历观察者的集合调用观察者中的更新(update)方法更新相关的内容;目标对象包含三个主要的方法:registerObserver(注册观察者,添加观察者到集合中)、unregisterObserver(注销观察者,从集合中移除观察者)、notifyObservers(通知观察者内容发生了变化);方法名是自己定义的,没有特殊的要求,只要能说明方法的内容就行。观察者(Observer)中包含一个主要的方法:update(更新观察者的内容)。当目标对象调用自身的notifyObserver时,遍历观察者集合,观察者对象调用自身的update方法,从而实现观察者模式。下图是观察者模式的示意图:
了解了上面的实现原理,下面写一个大致的实现观察者模式的框架,首先是目标对象父类(Subject):
import java.util.List;
import java.util.ArrayList;
/**
* 类描述: 目标对象,他知道观察它的观察这,并提供注册和删除观察者的接口
* 项目名称: ObserverDemo
* 类名称: Subject
* 创建人: xhl
* 创建时间: 2015-6-25 下午3:41:38
* 版本: v1.0
*/
public class Subject {
/**
* 存放所有观察者的集合
*/
private List<Observer> observers = new ArrayList<Observer>();
/**
* 添加新的观察者对象到观察者集合中去
* @param observer
*/
public void registerObserver(Observer observer){
observers.add(observer);
}
/**
* 移除制定的观察者对象
* @param observer
*/
public void unregisterObserver(Observer observer){
observers.remove(observer);
}
/**
* 提醒所有的观察者内容发生了变化
*/
public void notifyObservers(){
for (Observer observer : observers) {
observer.update(this);
}
}
}
<pre name="code" class="java">/**
* 类描述: 观察者接口
* 项目名称: ObserverDemo
* 类名称: Observer
* 创建人: xhl
* 创建时间: 2015-6-25 下午3:43:24
* 版本: v1.0
*/
public interface Observer {
abstract void update(Subject subject);
}
接下来定义目标对象的具体实现子类,用来改变该类的对象的状态,同时通知在改对象上注册的所有观察者,代码如下:
/**
* 类描述: 具体的目标对象, 目标对象的内容发生变化会提醒观察者进行相关状态的改变
* 项目名称: ObserverDemo
* 类名称: ConcreteSubject
* 创建人: xhl
* 创建时间: 2015-6-25 下午5:21:55
* 版本: v1.0
*/
public class ConcreteSubject extends Subject {
/**
* 目标对象的状态
*/
private String subjectState;
/***
* 获取目标对象的状态
* @return
*/
public String getSubjectState() {
return subjectState;
}
/**
* 设置目标对象的状态
* @param subjectState
*/
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
//更改了目标对象的状态,通知所有的观察者,目标对象的状态发生了改变
notifyObservers();
}
}
具体的目标对象中维护了一个自身的状态,当更改了这个状态,则调用notifyObservers()方法,通知所有的观察者对象,这个subjectState一般是观察者主要关注的被观察则对象的变量内容,它可以是一个单纯的状态,也可以是一个集合数据;在看这个观察者模式,感觉Android里面的ListView就是使用了这种模式,它里面的被观察对象就是填充到ListView里面的数据的集合,有时间了要去研究一下ListView的源码;下面是观察者的具体观察者对象,代码也很简单,如下:
/**
* 类描述: 具体的观察这对象
* 项目名称: ObserverDemo
* 类名称: ConcreteObserver
* 创建人: xhl
* 创建时间: 2015-6-25 下午5:16:04
* 版本: v1.0
*/
public class ConcreteObserver implements Observer {
/**
* 观察者对象的本身的状态
*/
private String concreteObserverState;
@Override
public void update(Subject subject) {
concreteObserverState = ((ConcreteSubject)subject).getSubjectState();
System.out.println(concreteObserverState);
}
}
观察者对象维护了一个自身的状态,根据目标对象(被观察者)的状态的改变来改变自己的状态,这里需要将subject强转为具体的观察者对象,以获取里面的内容;至此观察者模式的框架就完成了,这样当目标对象中的状态发生变化时就会通知观察者,从而实现观察者自身状态的改变,但是前提条件是这个观察者对象需要注册到目标对象(被观察者)上;下面是用这个观察者模式的框架实现一个天气发生变化时通知不同的观察者,从而观察者做出不同的反应:
首先是目标对象(被观察者),这里面的内容同框架中的目标对象是一致的:
package com.summerxia.wetherobserver;
import java.util.ArrayList;
import java.util.List;
/**
* 类描述: 观察的对象
* 项目名称: WetherObserverProject
* 类名称: Subject
* 创建人: xhl
* 创建时间: 2015-6-25 下午5:37:20
* 版本: v1.0
*/
public class Subject {
/**
* 所有观察者的对象集合
*/
private List<Observer> observers = new ArrayList<Observer>();
/**
* 注册一个观察者
* @param observer
*/
protected void registerObserver(Observer observer){
observers.add(observer);
}
/**
* 注销观察者
* @param observer
*/
protected void unregisterObserver(Observer observer){
observers.remove(observer);
}
/**
* 通知观察者天气发生了变化
*/
protected void notifyWetherChanged(){
for (Observer observer : observers) {
observer.update(this);
}
}
}
其次是观察者(Observer)的内容,同框架的也是一样的:
package com.summerxia.wetherobserver;
/**
* 类描述: 观察者的接口对象;
* 项目名称: WetherObserverProject
* 类名称: Observer
* 创建人: xhl
* 创建时间: 2015-6-25 下午5:39:10
* 版本: v1.0
*/
public interface Observer {
abstract void update(Subject subject);
}
最后是目标对象和观察者的具体实现者:
package com.summerxia.wetherobserver;
/**
* 类描述: 具体的观察对象
* 项目名称: WetherObserverProject
* 类名称: WetherSubject
* 创建人: xhl
* 创建时间: 2015-6-25 下午5:45:33
* 版本: v1.0
*/
public class WetherSubject extends Subject {
/**
* 天气的情况的内容
*/
private String wetherContent;
/**
* 获取天气情况
* @return
*/
public String getWetherContent() {
return wetherContent;
}
/**
* 设置天气状况
* @param wetherContent
*/
public void setWetherContent(String wetherContent) {
this.wetherContent = wetherContent;
this.notifyWetherChanged();
}
}
package com.summerxia.wetherobserver;
/**
* 类描述: 具体的天气观察者
* 项目名称: WetherObserverProject
* 类名称: WetherObserver
* 创建人: xhl
* 创建时间: 2015-6-25 下午10:13:13
* 版本: v1.0
*/
public class WetherObserver implements Observer {
/**
* 观察者的名称
*/
private String observerName;
/**
* 观察者要做的事情
*/
private String observerThing;
/**
* 观察者观察到的天气状况
*/
private String observerWetherContent;
@Override
public void update(Subject subject) {
observerWetherContent = ((WetherSubject)subject).getWetherContent();
System.out.println(observerName+"收到了短息:"+observerWetherContent+","+observerThing);
}
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
public String getObserverThing() {
return observerThing;
}
public void setObserverThing(String observerThing) {
this.observerThing = observerThing;
}
public String getObserverWetherContent() {
return observerWetherContent;
}
public void setObserverWetherContent(String observerWetherContent) {
this.observerWetherContent = observerWetherContent;
}
}
下面写一个类,编写main函数类测试观察者类的执行情况:
package com.summerxia.wetherobserver;
public class Main {
public static void main(String[] args) {
//1、创建一个目标对象
WetherSubject wether = new WetherSubject();
//2、创建观察者对象,并设置相关属性
WetherObserver observerGirl = new WetherObserver();
observerGirl.setObserverName("女朋友");
observerGirl.setObserverThing("适合约会,今天下午五点在中山公园小门见,不见不散哈!");
WetherObserver observerMum = new WetherObserver();
observerMum.setObserverName("老妈");
observerMum.setObserverThing("适合购物,今天可以去超市扫货");
//3、注册观察者
wether.registerObserver(observerGirl);
wether.registerObserver(observerMum);
//4、修改目标对象内容,通知观察者内容的改变
wether.setWetherContent("今天天气晴朗万里无云,最高气温25°C,最低气温15°C,");
}
}
运行的结果如下:
女朋友收到了短息:今天天气晴朗万里无云,最高气温25°C,最低气温15°C,,适合约会,今天下午五点在中山公园小门见,不见不散哈!
老妈收到了短息:今天天气晴朗万里无云,最高气温25°C,最低气温15°C,,适合购物,今天可以去超市扫货
这样就完成了一个简单的观察模式。Java本身定义好了目标对象父类及观察的接口,分别是:Observable和Observer,这个类和接口都是位于java.util包下的,在java中实现观察者模式不需要自己编写目标对象父类(Subject)和观察者接口(Observer),只需要编写具体的子类来继承类或实现接口即可;同样是上面的例子,用java本身的类实现如下:
package com.sumerxia.javaobserver;
import java.util.Observable;
/**
* 类描述: 具体的天气的目标对象
* 项目名称: JavaObserverDemo
* 类名称: WetherSubject
* 创建人: xhl
* 创建时间: 2015-6-25 下午10:20:16
* 版本: v1.0
*/
public class WetherSubject extends Observable {
/**
* 天气的内容
*/
private String wetherContent;
public String getWetherContent() {
return wetherContent;
}
public void setWetherContent(String wetherContent) {
this.wetherContent = wetherContent;
this.setChanged();
//提拉式通知
this.notifyObservers();
//推送式通知
// this.notifyObservers(wetherContent);
}
}
注意:在继承Observable类更改正太信息是,在调用notifyObservers()方法之前,需要调用setChanged()方法,否则notifyObservers不会执行,追踪到源码中可以看到notifyObservers()方法调用了notifyObservers(Object arg)方法,而在notifyObservers(Object arg)中,当changed为false时就直接跳出方法了(return掉),切记这一点;
另外你可能注意到了在上面的代码中写道了提拉式通知和推送式通知,这是观察者模式显示内容更新的两种方式。提拉式通知:是目标对象将自身传递给观察者,观察者拿到目标对象后,使用目标对象中的相关方法获取更改的内容等;推送式通知:是目标对象将具体的更改内容(观察者关注的内容),而非自身传递给观察者,观察者直接拿到更改的内容了;这个两个方法只能调用一个,不同同时调用,在观察者的update方法中存在两个参数,非别对应两种方式传递的内容。观察者子类的代码实现如下:
package com.sumerxia.javaobserver;
import java.util.Observable;
import java.util.Observer;
/**
* 类描述: 具体的观察者对象
* 项目名称: JavaObserverDemo
* 类名称: WetherObserver
* 创建人: xhl
* 创建时间: 2015-6-25 下午10:21:40
* 版本: v1.0
*/
public class WetherObserver implements Observer {
/**
* 观察者名称
*/
private String observerName;
/**
* 天气内容
*/
private String wetherContent;
/**
* 提醒的内容
*/
private String remindThing;
@Override
public void update(Observable o, Object arg) {
// wetherContent = (String) arg;
wetherContent = ((WetherSubject)o).getWetherContent();
System.out.println(observerName + "收到了消息:" + wetherContent + ","
+ remindThing);
}
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
public String getRemindThing() {
return remindThing;
}
public void setRemindThing(String remindThing) {
this.remindThing = remindThing;
}
}
在目标对象通知观察者时调用的提拉式的通知,所以在观察者对象的update方法中,需要使用目标对象来获取更新的内容;如果目标对象使用了推送式通知,则在观察者对象中的update方法中,直接拿更改的内容Object arg就可以了,这里就不再赘述了;
以上就是对于观察者模式的总结了,至于观察者模式什么时候使用,总结如下,大家可以参考一下:
1、当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化
2、如果在更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该 有多少对象需要被连带改变
3、当一个对象必须通知其他的对象,但是你又希望这个对象和其他的被通知的对象是松散耦合的