设计模式——观察者模式

观察者模式

这篇博文是根据B站尚硅谷设计模式课程韩老师所讲而做的,在此表示感谢!


观察者模式:对象之间多对一依赖的一种设计方案, 被依赖的对象为 Subject依赖的对象为 ObserverSubject通知 Observer 变化,比如奶站\气象局\报社等就类似于 Subject,Subject是 1 的一方,Observer是用户是多的一方;

基本介绍:

  • 观察者模式类似 订奶\订报\天气平台等业务
  • Subject:主要是登记注册、移除和通知Observer(观察者),主要有如下方法:
    • registerObserver() 注册观察者
    • removeObserver() 移除观察者
    • notifyObservers() 通知所有的注册的用户,根据不同需求,可以是更新数据,让用户来取,也可能是实施推送, 看具体需求定;
  • Observer:观察者,主要接收Subject的通知;

案例介绍

我们通过如下一个案例来演示观察者模式,问题如下:

  • 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方);
  • 需要设计开放型 API,便于其他第三方也能接入气象站获取数据;
  • 提供温度、气压和湿度的接口;
  • 测量数据更新时,要能实时的通知给第三方;

UML

在这里插入图片描述

编码

编写观察者的接口类

观察者的接口类Observer如下所示:

package edu.hebeu.observer.observer;

/**
 * 观察者接口
 * @author 13651
 *
 */
public interface Observer {
	
	void update(float temperature, float pressure, float humidity);
}

编写管理观察者的接口

管理观察者的接口Subject,包括了对观察者的注册,删除,通知等方法,如下

package edu.hebeu.observer.subject;

import edu.hebeu.observer.observer.Observer;

/**
 * 
 * @author 13651
 *
 */
public interface Subject {
	
	/**
	 * 注册观察者
	 * @param observer
	 */
	void registerObserver(String observerName, Observer observer);
	
	/**
	 * 删除观察者
	 * @param observer
	 */
	void removeObserver(String observerName);
	
	/**
	 * 通知所有的观察者
	 */
	void notifyObservers();
}

编写能够测量、修改天气数据的类

该类嗨需要实现Subect接口,使该类能够获得其内部的特性,如,注册、删除、通知观察者,以保证在每次数据更新时数据能实时的发送至观察者,让观察者将最新的数据展示,代码如下:

package edu.hebeu.observer.subject;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import edu.hebeu.observer.observer.Observer;

public class WeatherData implements Subject{
	
	// 温度,气压,湿度
	private float temperatrue; 
	private float pressure; 
	private float humidity;
	
	/**
	 * 观察者集合
	 */
	private Map<String, Observer> observers;
	
	public WeatherData() {
		observers = new HashMap<>();
	}
	
	/**
	 * 获取温度
	 * @return
	 */
	public float getTemperature() {
		return temperatrue;
	}

	/**
	 * 获取气压
	 * @return
	 */
	public float getPressure() {
		return pressure;
	}

	/**
	 * 获取湿度
	 * @return
	 */
	public float getHumidity() {
		return humidity;
	}

	/**
	 * 当数据有更新时,调用的函数
	 * @param temperature
	 * @param pressure
	 * @param humidity
	 */
	public void setData(float temperature, float pressure, float humidity) {
		this.temperatrue = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		//调用 notifyObservers, 将最新的信息 推送给 接入方 所有的观察者
		notifyObservers();
	}

	@Override
	public void registerObserver(String observerName, Observer observer) {
		observers.put(observerName, observer);
		System.out.println("添加《" + observerName + "》观察者成功!");
	}

	@Override
	public void removeObserver(String observerName) {
		if(observers.containsKey(observerName)) {
			observers.remove(observerName);
			System.out.println("删除《" + observerName + "》观察者成功!");
			return;
		}
		System.err.println("未找到《" + observerName + "》观察者,删除失败!");
	}

	@Override
	public void notifyObservers() {
		Set<Map.Entry<String, Observer>> observerSet = observers.entrySet();
		for(Map.Entry<String, Observer> observer : observerSet) {
			observer.getValue().update(this.temperatrue, this.pressure, this.humidity);
		}
	}

}

编写"第三方的接口“

实际上就类似于MySQL、Oracle实现Java提供的数据库驱动一样,我们为了模拟需要提供如下的几个类:
BaiDu

package edu.hebeu.observer.observer;

public class BaiDu implements Observer{
	
	// 温度,气压,湿度
	private float temperature; 
	private float pressure; 
	private float humidity;

	/**
	 * 更新天气情况,是由 WeatherData的对象实例来调用,即使用推送模式
	 * @param temperature
	 * @param pressure
	 * @param humidity
	 */
	@Override
	public void update(float temperature, float pressure, float humidity) {
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		display();
	}

	/**
	 * 显示数据
	 */
	public void display() {
		System.out.println("========================百度天气=========================");
		System.out.println("***Today mTemperature: " + temperature + "***"); 
		System.out.println("***Today mPressure: " + pressure + "***"); 
		System.out.println("***Today mHumidity: " + humidity + "***");
	}

}

WangYi

package edu.hebeu.observer.observer;

public class WangYi implements Observer{
	// 温度,气压,湿度
	private float temperature; 
	private float pressure; 
	private float humidity;

	/**
	 * 更新天气情况,是由 WeatherData的对象实例来调用,即使用推送模式
	 * @param temperature
	 * @param pressure
	 * @param humidity
	 */
	@Override
	public void update(float temperature, float pressure, float humidity) {
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		display();
	}

	/**
	 * 显示数据
	 */
	public void display() {
		System.out.println("========================网易天气=========================");
		System.out.println("&&&Today mTemperature: " + temperature + "***"); 
		System.out.println("***Today mPressure: " + pressure + "---"); 
		System.out.println("^^^Today mHumidity: " + humidity + "***");
	}
}

XinLang

package edu.hebeu.observer.observer;

public class XinLang implements Observer{
	// 温度,气压,湿度
	private float temperature; 
	private float pressure; 
	private float humidity;

	/**
	 * 更新天气情况,是由 WeatherData的对象实例来调用,即使用推送模式
	 * @param temperature
	 * @param pressure
	 * @param humidity
	 */
	@Override
	public void update(float temperature, float pressure, float humidity) {
		this.temperature = temperature;
		this.pressure = pressure;
		this.humidity = humidity;
		display();
	}

	/**
	 * 显示数据
	 */
	public void display() {
		System.out.println("========================新浪天气=========================");
		System.out.println("---Today mTemperature: " + temperature + "---"); 
		System.out.println("***Today mPressure: " + pressure + "***"); 
		System.out.println("+++Today mHumidity: " + humidity + "+++");
	}
}

可以发现我们需要什么订阅者,直接就实现Observer接口就好了,非常方便;

编写测试类

编写上述代码的测试类Client,如下所示:

package edu.hebeu.observer;

import java.util.Scanner;

import edu.hebeu.observer.observer.BaiDu;
import edu.hebeu.observer.observer.WangYi;
import edu.hebeu.observer.observer.XinLang;
import edu.hebeu.observer.subject.WeatherData;

public class Client {
	
	private static float TEMPERATURE, PRESSURE, HUMIDITY; // 声明保持温度、湿度、气压的静态变量
	
	public static void main(String[] args) {
		WeatherData weatherData = new WeatherData();
		
		// 注册初始的观察者
		weatherData.registerObserver("百度天气", new BaiDu());
		weatherData.registerObserver("新浪天气", new XinLang());
		weatherData.registerObserver("网易天气", new WangYi());
		
		// 更新信息
		weatherData.setData(37f, 158.9f, 29f);

		Scanner scanner = new Scanner(System.in);
		while(true) {
			System.out.println();System.out.println();System.out.println();
			System.out.println("\"u(update)\"更新数据");
			System.out.println("\"a(add)\"添加观察者");
			System.out.println("\"r(remove)\"删除观察者");
			System.out.println("\"e(exit)\"退出程序");
			
			System.out.print("请输入:");Character keyword = scanner.next().charAt(0);
			
			if(keyword.equals('u')) {
				System.out.print("温度:"); TEMPERATURE = scanner.nextFloat();
				System.out.print("湿度:"); PRESSURE = scanner.nextFloat();
				System.out.print("气压:"); HUMIDITY = scanner.nextFloat();
				
				System.out.println("--------------------------------------------天气更新-------------------------------");
				weatherData.setData(TEMPERATURE, PRESSURE, HUMIDITY); // 改变数据
			} else if(keyword.equals('a')) {
				System.out.print("请输入添加的观察者名:"); String observerName = scanner.next();
				if(observerName.equals("百度天气")) {
					weatherData.registerObserver(observerName, new BaiDu());
				} else if(observerName.equals("新浪天气")) {
					weatherData.registerObserver(observerName, new XinLang());
				} else if(observerName.equals("网易天气")) {
					weatherData.registerObserver(observerName, new WangYi());
				} else {
					System.err.println("第三方库中未找到" + observerName + "接口,添加失败!");
				}
			} else if(keyword.equals('r')) {
				System.out.print("请输入删除的观察者名:"); String observerName = scanner.next();
				weatherData.removeObserver(observerName);
			} else if(keyword.equals('e')) { // 如果输入是 "exit"
				break; // 退出循环
			}
		}
		if(scanner != null) {
			scanner.close();
		}
		System.out.println("bye~~");
	}
}

测试

测试更新天气数据,就会自动的通知其他的观察者改变它们的数据,然后将最新的数据显示出来,如下所示:
在这里插入图片描述
也可以将某些注册的观察者删除,如下:
在这里插入图片描述
也可以添加观察者,但是要保证有”该观察者“,如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值