首先,本博客是根据慕课网http://www.imooc.com/learn/415 这个教学后的一些记录。方便以后回顾。
简介
观察者模式,有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
上面说的还是比较抽象,举个例子,一个苦逼的程序员黄明要给老妈和女朋友发送每天的天气预报,有些时候要给老妈发,有时候要给女朋友发。这时候老妈和女朋友就是我要发送消息对象的观察者(订阅者)Observer。而天气预报信息就是消息发布者Subject。
- 黄明获取了最新的天气预报可以推送给老妈和女朋友
- 黄明的女朋友设置了约会的时间地点,黄明可以发消息单独给女朋友发送
- 黄明的老妈则喜欢逛街,于是设置了购物提醒
观察者收到通知以后,可以各自做出自己的反应。
下图是观察者模式的结构:
其中Attach是添加观察者,Detach时删除观察者,Notify负责通知观察者。当Observer收到Notify的内容后,使用Update进行更新。
使用java实现基本的观察者模式
步骤:
- 目标对象的定义
- 具体目标对象的定义
- 观察者接口的定义
- 观察者具体实现
目标对象的定义
package observer_pattern;
import java.util.ArrayList;
import java.util.List;
/**
* 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者接口
* @author han
*
*/
public class Subject {
private List<Observer> observers=new ArrayList<Observer>(); //用来保存注册的观察者对象
/**
* 将一个观察者对象add进观察者集合中
* @param observer
*/
public void attach(Observer observer){
observers.add(observer);
}
/**
* 将观察者对象从观察者集合中删除
* @param observer
*/
public void detach(Observer observer){
observers.remove(observer);
}
/**
* 向所有注册的观察者发送消息
*/
protected void notifyObservers(){
for(Observer observer:observers){
observer.update(this);
}
}
}
具体目标对象的定义
package observer_pattern;
/**
* 具体的目标对象,负责把有关状态存入到相应的观察者对象中
* @author han
*
*/
public class ConcreteSubject extends Subject {
private String subjectState; //目标对象的状态
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
this.notifyObservers();//通知观察者
}
public ConcreteSubject() {
// TODO Auto-generated constructor stub
}
}
观察者接口的定义
package observer_pattern;
/**
* 观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
* @author han
*
*/
public interface Observer {
/**
* 更新的接口
* @param subject传入目标对象,方便获取相应的目标对象的对象
*/
public void update(Subject subject);
}
观察者具体实现
package observer_pattern;
/**
* 具体观察者对象,实现更新的方法,使自身的状态和目标的状态保持一致
* @author han
*
*/
public class ConcreteObserver implements Observer {
private String observerState;
public ConcreteObserver() {
// TODO Auto-generated constructor stub
}
/**
* 同步目标类的状态同步到观察者的状态中
*/
@Override
public void update(Subject subject) {
// TODO Auto-generated method stub
observerState=((ConcreteSubject)subject).getSubjectState();
}
}
整体的四个实现如上,Subject的attach和detach将观察者Observer从列表中添加和删除,notifyObservers方法将observer更新。
推模型与拉模型
上面只是模板,在写实例之前,先讲解推模型和拉模型两种概念:
推模型:将需要传送的数据直接推送给观察者。update方法中传递的参数是要传输的数据。主题对象向观察者推送主题的详细信息,不管观察者是否需要,推送的信息通常是主题对象的全部或部分数据。(假定目标对象知道观察者需要的数据)
拉模型:主题对象在通知观察者的时候,只传递少量信息。如果观察者需要更具体的信息,由观察者主动到主题对象中获取,相当于是观察者从主题对象中拉数据。一般这种模型的实现中,会把主题对象自身通过update()方法传递给观察者,这样在观察者需要获取数据的时候,就可以通过这个引用来获取了。(目标对象不知道观察者具体需要什么数据,因此把自身传递给观察者,由观察者来取值)
实例:
下面是一个实例:
一个Subject用与发布天气(Weather)消息,Observer用于接收消息
首先是WeatherSubject(天气发布者):
package ObserverPatternCommon;
import java.util.ArrayList;
import java.util.List;
/**
* 目标对象,它知道观察它的观察者,并提供注册(添加)和删除观察者借口
* @author han
*
*/
public class WeatherSubject {
private List<Observer> observers=new ArrayList<Observer>(); //用来保存注册的观察者对象
/**
* 将订阅天气的人天价到订阅者列表中
* @param observer
*/
public void attach(Observer observer){
observers.add(observer);
}
/**
* 删除集合中的指定订阅天气的人
* @param observer
*/
public void detach(Observer observer){
observers.remove(observer);
}
/**
* 通知所有已经订阅了天气的人,拉模型
*/
// protected void notifyObservers(){
// for(Observer observer:observers){
// observer.update(this); //传递的是发布者自身
// }
// }
//推模型
protected void notifyObservers(String content){
for(Observer observer:observers){
observer.update(content);//传递的只是数据
}
}
}
ConcreteWeatherSubject(WeatherSubject天气发布者具体实现类)
package ObserverPatternCommon;
/**
* 具体的目标对象,负责把有关状态存入到相应的观察者对象中
* @author han
*
*/
public class ConcreteWeatherSubject extends WeatherSubject {
private String weatherContent; //获取天气内容信息
public String getWeatherContent() {
return weatherContent;
}
public void setWeatherContent(String weatherContent) {
this.weatherContent = weatherContent;
// this.notifyObservers();//拉模型
this.notifyObservers(weatherContent);
}
}
Observer.java,接口类
package ObserverPatternCommon;
/**
* 观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
* @author han
*
*/
public interface Observer {
/**
* 更新的接口
* @param subject传入目标对象,方便获取相应的目标对象的对象
*/
//public void update(WeatherSubject subject); //拉模型
public void update(String content); //推模型
}
ConcreteObserver.java
package ObserverPatternCommon;
/**
* 具体观察者对象,实现更新的方法,使资深的状态和目标的状态保持一致
*
* @author han
*
*/
public class ConcreteObserver implements Observer {
// 所需变量:1.观察者的名字2.天气情况的内容3.提醒的内容
private String observerName;
private String weatherContent;
private String remindThing;
/**
* 同步目标类的状态同步到观察者的状态中,拉模型
*/
// @Override
// public void update(WeatherSubject subject) {
// // TODO Auto-generated method stub
// //只有weatherContent时从目标处获取的
// weatherContent=((ConcreteWeatherSubject)subject).getWeatherContent();
// //在update中打印观察者收到的天气情况,做什么事情
// System.out.println(observerName+"收到了"+weatherContent+","+remindThing);
// }
// 推模型
@Override
public void update(String content) {
// TODO Auto-generated method stub
weatherContent = content;
// 在update中打印观察者收到的天气情况,做什么事情
System.out.println(observerName + "收到了" + weatherContent + "," + remindThing);
}
public String getObserverName() {
return observerName;
}
public void setObserverName(String observerName) {
this.observerName = observerName;
}
public String getWeatherContent() {
return weatherContent;
}
public void setWeatherContent(String weatherContent) {
this.weatherContent = weatherContent;
}
public String getRemindThing() {
return remindThing;
}
public void setRemindThing(String remindThing) {
this.remindThing = remindThing;
}
}
测试类Client.java
package test;
import ObserverPatternCommon.ConcreteObserver;
import ObserverPatternCommon.ConcreteWeatherSubject;
public class Client {
public static void main(String[] args) {
//1.创建目标
ConcreteWeatherSubject weather=new ConcreteWeatherSubject();
//2.创建观察者
ConcreteObserver observerGril=new ConcreteObserver();
observerGril.setObserverName("女朋友");
observerGril.setRemindThing("是我们第一次约会,地点街心公园,不见不散哦");
ConcreteObserver observerMum=new ConcreteObserver();
observerMum.setObserverName("老妈");
observerMum.setRemindThing("是一个购物的好日子,明天去购物");
//3.注册观察者
weather.attach(observerGril);
weather.attach(observerMum);
//4.在目标处发布天气
weather.setWeatherContent("明天天气晴朗,蓝天白云,气温28度");
}
}
运行client的最终结果(拉模型与推模型运行本例运行结果相同):
我们看到,最终消息发布者将消息发送给了订阅者。
观察者模式的优点:
- 观察者模式实现了观察者与目标之间的抽象解耦
- 观察者模式实现了动态联动
- 观察者模式支持广播通信
- 可能会引起无谓的操作
- 当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化
- 如果在更改一个对象的时候,需要同时连带改变其它的对象,而且不知道究竟应该有多少对象需要被连带改变
- 档一个对象必须同志其他的对象,但是你又希望这个对象和其它被通知的对象是松散耦合的