HF_观察者模式(Observer Pattern) _20200301

观察者模式的定义:

                     观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

                主题是一个接口,“观察”也是一个接口,“观察接口”是“主题接口”的方法中的参数,是利用多态 将 “观察者的实例” 传入主题者的“方法参数”中的。人人皆可主题者,人人皆可观察者,实现接口就行。观察者换个名字,叫订阅者会容易理解很多,就像订阅报纸一样。

              “主题者的类定义” 和 “观察者的类定义” 之间的通信主要是通过,主题接口和观察接口,进行。即是通过第三者接口实现了松耦合。

               核心就是,都是操作接口的方法,而不是直接操作类。由于接口的抽象方法引起具体实现类的方法的执行,类是被动的。

示例需求:

           一个气象站,写一个应用软件来显示三个传感器探测到的气象信息,气象站通过传感器获取天气信息,所以你要建造的软件就是从三个传感器中获取信息,并且通过三个用户界面(布告板)把天气信息展示出来。天气信息主要是三种,当前状况,气象统计,天气预报。

需求分析:

         现在气象局告诉你,你要定义一个WeatherData类,且该类中有measurementsChanged()方法,我一旦有气象信息更新,就会自动调用你的measurementsChanged()方法了。

        所以你可以从该类中的measurementsChanged()方法中获取各个气象站的气象信息,先别管measurementsChanged()方法是怎么被自动调用的,可以是在java的mian方法中人工被调用行不行?然后你要做的就是,如何通过在measurementsChanged()方法中通知三个用户界面(布告板),气象信息更新了。

模式的概念:

          出版者+订阅者 = 观察者模式 , 只是这里在模式的术语上,出版者称作“主题(subject)”, 订阅者称作“观察者(Observer)”,也就是说WeatherData是一个主题者,三块布告板是三个观察者。

模式的巧妙之处:

            在于观察者是如何注册成为主题的观察者的,也就是如何订阅主题的。观察者是将主题者的一个实例作为参数,传入到观察者的注册方法中。或者另一种方式,观察者将自身实例作为一个参数,传入到主题者的注册方法中。所以,全局环境必须要有一个主题者实例。

             注意,是主题者的实例,不是定义,是已经实现了的。主题者一般定义为一个接口,这样人人都可以成为主题者,只要遵循主题的接口,实现主题接口里面的方法就可以了。

            注册的方法一般都是,观察者将自身实例作为一个参数,传入到主题者的注册方法中。所以主题接口在设计的时候,就需要把相关方法的参数设置为观察者接口,没错,观察者一般也是一个接口。实际运行代码的时候,是利用多态将观察者的实例传入主题者的方法参数中的

         所以就是(主题者和观察者的)接口、接口,类定义、类定义,实例、实例。

        主题者负责维护观察者的数组,也就是每注册一个观察者,主题者就把观察者的引用保存到自己的数组中,删除同理。

        主题接口只使用观察接口的抽象方法。

         观察者的类定义中用变量维护了主题接口的引用,利用多态,用于注册、取消订阅等操作。(这里只是定义,真正的执行都是main方法中的主题者实例和观察者实例)。

         这么说的话,“主题者的类定义”是利用 “抽象方法的参数” 维护了观察者接口的引用。

         ★ 所以说,主题者发布某一个通知,就是调用了主题接口里的“主题抽象方法”,而“主题抽象方法”的参数又是观察者接口,然后主题者的类定义中 “主题抽象方法”的 “具体实现方法” 就可以通过这个参数 任意调用 “观察者接口中的抽象方法”,所以“具体观察者类的 具体实现方法”必然就被调用了,因为具体类必须实现接口的抽象方法嘛。所以总得来看,主题者实例的某方法的执行,就会链式地引起观察者实例的某方法的执行。也就是发布了通知了。其实画图出来一目了然,自己画去。

示例代码:

主题者的接口、类定义、(实例在main方法中):

package s_Interface;
/**
 * 用于提供所有想成为主题者都必须实现的接口
 * @author mathew
 *
 */
public interface Subject {
	
	public void registerObserver(Observer o);
	public void removeObserver(Observer o);
	public void notifyObservers();

}
package s_ImpolementInterfaceClass;

import java.util.ArrayList;

import s_Interface.Observer;
import s_Interface.Subject;
/**
 * 主题者,主要工作是发布通知,提供方法给观察者注册、移除。实际就是main方法中调用主题者实例的方法咯。
 *
 */
public class WeatherData implements Subject {
	
	private ArrayList observers; //观察者数组,主题的观察者们容身之地
	private float temperature;
	private float humidity;
	private float pressure;
	
	public WeatherData() {
		// TODO Auto-generated constructor stub
		observers = new ArrayList();
	}
	

	@Override
	public void registerObserver(Observer o) {
		// TODO Auto-generated method stub
		observers.add(o);

	}

	@Override
	public void removeObserver(Observer o) {
		// TODO Auto-generated method stub
		int i = observers.indexOf(o);
		if (i >= 0) {
			observers.remove(i);
		}
	}

	/**
	 * 主题者会调用observer接口的的upadate方法,而upadte方法看是谁实现,如果是在当前类中实现的,那就调用当前类实现的upadate方法。
	 * 也就是利用了多态,利用了观察类实现了observer接口,而主题者只维护observer接口的抽象对象,而不维护observer接口的具体实现的对象。
	 * 然后,主题者就发通知给observer接口各个抽象对象们,发通知的形式就是调用observer接口的update方法
	 */
	@Override
	public void notifyObservers() {
		// TODO Auto-generated method stub
		for (int i = 0; i < observers.size();i++) {
			Observer observer = (Observer) observers.get(i);
			observer.update(temperature, humidity, pressure);
		}
	}
	
	//当测量数据发生变化时,就通知观察者
	public void measurementsChanged() {
		notifyObservers();
	}

	//为了模拟气象站传感器检测获取到的数据,现在只好人工设置了咯
	public void setMeasurements(float temperature, float humidity,float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}
}

观察者的接口、类定义、(实例在main方法中):

package s_Interface;

/**
 * 该接口用于提供观察者和主题的共同接口,用于松耦合时彼此之间通过第三方进行沟通
 *
 */
public interface Observer {
	
	public void update(float temp, float humidity,float pressure);

}
package s_ImpolementInterfaceClass;

import s_Interface.*;
/**
 * 观察者,该类是一个展示当前天气情况的展示板,自己并非主题者,但是内部维护了一个主题者的引用,等待主题者发布通知后。对通知的内容进行处理
 * @author mathew
 *
 */
public class CurrentConditionsDisplay implements DisplayElement, Observer {
	
	private float temperature;
	private float humidity;
	private Subject weatherData;//主题者,其实是保留了主题者的引用
	
	//构造函数,初始化时,获取主题者的引用,并负责维护该引用
	public CurrentConditionsDisplay(Subject weatherData) {
		// TODO Auto-generated constructor stub
		this.weatherData = weatherData;
		this.weatherData.registerObserver(this); //自己注册成为观察者,其实更好的设计不是在构造方法中,而是抽取出来别的方法
		
	}

	//观察者在这里具体实现公共接口的方法,因为主题者会调用公共接口的抽象方法,由于多态,所以就是调用了下面的方法实现代码了
	@Override
	public void update(float temperature, float humidity, float pressure) {
		// TODO Auto-generated method stub
		this.temperature = temperature;
		this.humidity = humidity;
		display();

	}

	@Override
	public void display() {
		// TODO Auto-generated method stub
		System.out.println("Current conditons:" + temperature + " F degress, " + humidity + "% humidity" );
		System.out.println("亲,打印了CurrentConditionDisplay的信息了哟!!!!!!");
	}

}

主题者、观察者的实例,即是main方法:

package s_Class;

import s_ImpolementInterfaceClass.*;

/**
 * 那就用气象站对象来展示该模式吧,气象站赋值给WeatherData对象,然后响应的观察者就会有相应的动作了
 * @author mathew
 *
 */
public class WeatherStation {
	public static void main(String[] args) {
		//主题者对象,因为各个布告板都是观察这个天气信息的
		WeatherData weatherData = new WeatherData();
		//观察者对象,在构造方法中注册了主题,其实就是通过主题的引用,把自己添加在主题里的数组里了,也就是注册了。
		CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
		System.out.println("开启执行了啊!!");
		weatherData.setMeasurements(81, 65, 30.5f);
		weatherData.setMeasurements(89, 63, 31.5f);
		weatherData.setMeasurements(85, 66, 32.6f);
	}

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值