java观察者模式(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/specialshoot/article/details/50804304

首先,本博客是根据慕课网http://www.imooc.com/learn/415 这个教学后的一些记录。方便以后回顾。

简介

观察者模式,有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

上面说的还是比较抽象,举个例子,一个苦逼的程序员黄明要给老妈和女朋友发送每天的天气预报,有些时候要给老妈发,有时候要给女朋友发。这时候老妈和女朋友就是我要发送消息对象的观察者(订阅者)Observer。而天气预报信息就是消息发布者Subject。

  • 黄明获取了最新的天气预报可以推送给老妈和女朋友
  • 黄明的女朋友设置了约会的时间地点,黄明可以发消息单独给女朋友发送
  • 黄明的老妈则喜欢逛街,于是设置了购物提醒

观察者收到通知以后,可以各自做出自己的反应。

下图是观察者模式的结构:

观察者模式结构

其中Attach是添加观察者,Detach时删除观察者,Notify负责通知观察者。当Observer收到Notify的内容后,使用Update进行更新。

使用java实现基本的观察者模式

步骤:

  1. 目标对象的定义
  2. 具体目标对象的定义
  3. 观察者接口的定义
  4. 观察者具体实现
下面将观察者模式的模板给出

目标对象的定义

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的最终结果(拉模型与推模型运行本例运行结果相同):

运行结果

我们看到,最终消息发布者将消息发送给了订阅者。

观察者模式的优点:

  1. 观察者模式实现了观察者与目标之间的抽象解耦
  2. 观察者模式实现了动态联动
  3. 观察者模式支持广播通信
观察者模式的缺点

  1. 可能会引起无谓的操作
何时使用观察者模式:

  1. 当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化
  2. 如果在更改一个对象的时候,需要同时连带改变其它的对象,而且不知道究竟应该有多少对象需要被连带改变
  3. 档一个对象必须同志其他的对象,但是你又希望这个对象和其它被通知的对象是松散耦合的
下一篇系列文章将会使用java自带的观察者模式,并且会扩展一些功能,敬请期待!!!

猜你喜欢

转载自blog.csdn.net/specialshoot/article/details/50804304