文档章节

设计模式——观察者模式:天气推送的两种实现

AlanVision
 AlanVision
发布于 2016/10/14 17:16
字数 2079
阅读 4.3K
收藏 87

需求说明:

目前我们需要实现一种天气实时更新的程序(天气推送),当气象站数据更新后,天气接口程序去获取最新天气数据,然后将数据分发给所有订阅过“天气日报”程序的用户,即使更新数据。

(案例来源于《Head First 设计模式》)

整体结构图:

由上图可以看出,气象站负责去检测天气情况,当数据发生变化时,天气服务获取了变化的数据并且需要将数据分发给众多订阅“天气推送”的用户,如果我们采用一个个去通知的形式去实现的话,那么当我们新增了一个观察者,我们又需要单独重复编写发布信息代码,当我们想去掉一个观察者的时候,又需要去删减部分代码,这样的操作实在很繁琐,耦合度也比较高。接下来我们采用观察者模式来实现这个需求,来看看有没有什么神奇的地方。

观察者模式

观察者模式定义了一系列对象之间的一对多关系,当一个对象(主题)改变状态,其他依赖者都会收到通知。

观察者模式组成

抽象主题角色:把所有对观察者对象的引用保存在一个集合中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。

抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。

具体主题角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。

具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。

观察者模式原型

观察者实现方式

1.主题主动推送数据

当数据更新后,主题会将所有更新数据都推送给观察者,而观察者只能被动接受主题的推送信息。

2.观察者主动拉取数据

当数据更新后,主题会通知观察者数据更新了,并暴露出相关数据的getter方法,观察者们可根据自己的需要去拉取自己需要的信息。

两种方式的代码实现

方式一:主题推送数据

这里我们采取自己编写抽象主题与抽象观察者代码的方式来实现。

抽象主题:SubjectInterface.java

package observer.one.subject;

import observer.one.observers.ObserverInterface;

public interface SubjectInterface {
	public void registerObserver(ObserverInterface o);
	public void removeObserver(ObserverInterface o);
	public void notifyObservers();
}

具体主题:WeatherData.java

package observer.one.subject.impl;

import java.util.ArrayList;

import observer.one.observers.ObserverInterface;
import observer.one.subject.SubjectInterface;

public class WeatherData implements SubjectInterface{
	
	private ArrayList<ObserverInterface> observers;
	//温度
	private String temperature;
	//湿度
	private String humidity;
	//气压
	private String pressure;
	
	public WeatherData() {
		observers=new ArrayList<ObserverInterface>();
	}
	/**
	 * 订阅
	 */
	public void registerObserver(ObserverInterface o) {
		observers.add(o);
	}
	/**
	 * 取消订阅
	 */
	public void removeObserver(ObserverInterface o) {
		if(observers.indexOf(o)>=0){
			observers.remove(o);
		}
	}
	/**
	 * 通知观察者
	 */
	public void notifyObservers() {
		for(ObserverInterface o:observers){
			o.update(temperature, humidity, pressure);
		}
	}
	public void setDataChange() {
		notifyObservers();
	}
	/**
	 * 数据改变后,通知观察者
	 * @param temperature
	 * @param humidity
	 * @param pressure
	 */
	public void setNewData(String temperature,String humidity,String pressure){
		this.temperature=temperature;
		this.humidity=humidity;
		this.pressure=pressure;
		setDataChange();
	}
}

抽象观察者:ObserverInterface.java

package observer.one.observers;

public interface ObserverInterface {
	void update(String temperature,String humidity,String pressure);
}

实际上,我们看到这里就会发现有点问题,一旦我们的数据参数发生变化,就需要去修改抽象观察者方法,而具体观察者也需要去修改相关方法的实现,耦合性较大。

具体观察者:Observer1.java

package observer.one.observers.impl;

import java.util.Date;

import observer.one.observers.ObserverInterface;
import observer.one.subject.SubjectInterface;

public class Observer1 implements ObserverInterface{
	
	private SubjectInterface subject;
	
	//温度
	private String temperature;
	//湿度
	private String humidity;
	//气压
	private String pressure;
	
	public Observer1(SubjectInterface subject) {
		this.subject = subject;
		subject.registerObserver(this);
	}
	
	public void register(){
		System.out.println("------观察者1订阅成功-------");
		subject.registerObserver(this);
	}
	
	public void cancelRegister(){
		System.out.println("------观察者1取消订阅了-------");
		subject.removeObserver(this);
	}
	
	public void update(String temperature, String humidity, String pressure) {
		this.humidity=humidity;
		this.pressure=pressure;
		this.temperature=temperature;
		display();
	}

	private void display() {
		System.out.println("观察者1----数据更新了("+new Date()+")-温度:"+temperature+"-湿度:"+humidity+"-气压:"+pressure);
	}
	
}

测试方法

这里为了方便演示,我写了两个具体观察者(Observer1 ,Observer2 ),实际上在具体实现中只需要写一个,实例化多个即可。

package observer.one;

import observer.one.observers.impl.Observer1;
import observer.one.observers.impl.Observer2;
import observer.one.subject.impl.WeatherData;

public class Test {
	
	public static void main(String[] args) {
		WeatherData weatherData=new WeatherData();
		//订阅天气日报
		Observer1 observer1=new Observer1(weatherData);
		//订阅天气日报
		Observer2 observer2=new Observer2(weatherData);
		
		weatherData.setNewData("10", "20", "30");
		observer1.cancelRegister();
		weatherData.setNewData("10.5", "14.23", "15.65");
		observer1.register();
		weatherData.setNewData("15.5", "18.23", "17");
	}
}

输出结果:

方式二:观察者主动拉取数据

这里我们采取JDK自己提供的抽象主题与抽象观察者的方式来实现。

抽象主题:java.util.Observable

抽象观察者:java.util.Observer

具体主题:WeatherData.java

package observer.two.subject;

import java.util.Observable;

public class WeatherData extends Observable{
	//温度
	private String temperature;
	//湿度
	private String humidity;
	//气压
	private String pressure;
	/**
	 * 数据更新方法
	 * @param temperature
	 * @param humidity
	 * @param pressure
	 */
	public void setNewData(String temperature,String humidity,String pressure){
		this.temperature=temperature;
		this.humidity=humidity;
		this.pressure=pressure;
		setChanged();
		notifyObservers("数据更新了,快来获取吧");
	}
	
	public String getTemperature() {
		return temperature;
	}
	public String getHumidity() {
		return humidity;
	}
	public String getPressure() {
		return pressure;
	}
}

由上面代码可以看出,具体主题主动暴露出属性的getter方法,当数据更新时调用setChange()方法,通知观察者此时有数据更新,你可以来获取了。

具体观察者:Observer1.java

package observer.two.observers;

import java.util.Date;
import java.util.Observable;
import java.util.Observer;

import observer.two.subject.WeatherData;

public class Observer1 implements Observer{
	
	private Observable observable;
	
	//温度
	private String temperature;
	//湿度
	private String humidity;
	//气压
	private String pressure;
		
	public Observer1(Observable observable) {
		super();
		this.observable = observable;
		System.out.println("------观察者1订阅了-------");
		observable.addObserver(this);
	}
	
	public void cancelRegister(){
		System.out.println("------观察者1取消订阅了-------");
		observable.deleteObserver(this);
	}
	/**
	 * 数据更新通知
	 */
	public void update(Observable o, Object arg) {
		System.out.println("气象台说:"+arg+"");
		System.out.println("观察者1:嗯,这就去");
		if(o instanceof WeatherData){
			WeatherData data=(WeatherData)o;
			this.humidity=data.getHumidity();
			this.pressure=data.getPressure();
			this.temperature=data.getTemperature();
		}
		display();
		System.out.println("数据提取完毕,并已展示");
	}
	/**
	 * 数据打印
	 */
	private void display() {
		System.out.println("观察者1----数据更新了("+new Date()+")-温度:"+temperature+"-湿度:"+humidity+"-气压:"+pressure);
	}

}

此时,具体观察者调用update方法,主动拉取主题的最新数据,并显示出来。

测试方法:

package observer.two;

import observer.two.observers.Observer1;
import observer.two.subject.WeatherData;

public class Test {
	public static void main(String[] args) {
		WeatherData weatherData=new WeatherData();
		/*观察者1订阅天气日报*/
		Observer1 observer1=new Observer1(weatherData);
		Observer1 observer2=new Observer1(weatherData);
		weatherData.setNewData("10", "79", "18");
	}
}

输出结果:

两种实现方式对比

细心的同学应该可以发现,自己编写抽象主题代码时,我们编写的接口(interface),而JDK官方提供的观察者模式中,抽象主题采用的是继承的形式实现。归根结底这两种实现方式的对比,是主题推送数据和观察者拉取数据的对比,以及实现接口和继承父类的对比。

推送数据和拉取数据的对比:

推送数据的实现方式主题需要把所有的参数都推送给观察者们,这里就需要事先在update方法中指定所有的参数,一旦参数发生改变,不仅要改变主题的方法,还需要改变观察者接口方法。
而拉取数据的实现方式只需要改变主题参数,不需要改变观察者接口方法,观察者们依旧根据自己的需要去获取数据。  

实现接口和继承父类的对比:

由于JAVA只支持单继承,实现接口和继承父类的优劣势大家也应该都很清楚。

对比结果:如果可以的话,我们应该自己去实现自己的抽象主题,而不采用JDK官方提供的实现方式,毕竟继承的形式有一定的弊端。在数据获取方面,最好采用具体观察者拉取的形式,这样更有利于后期进行扩展。具体的代码实现,我就不共享出来了,仔细看了这篇文章的人基本都可以自己去实现。

相关文章:《设计模式——策略模式:会员价格体系的简单实现

© 著作权归作者所有

AlanVision
粉丝 117
博文 51
码字总数 17340
作品 0
深圳
程序员
私信 提问
加载中

评论(4)

2
2010带你飞
麻烦博主把代码提交一下,正好需要这个~~~
L
LerbinWu
博主的编译器是什么
AlanVision
AlanVision 博主

引用来自“刘亚涛”的评论

话说博主的画图工具是什么啊?看着很高大上的感觉
大微软的visio
改个昵称吧
改个昵称吧
话说博主的画图工具是什么啊?看着很高大上的感觉
JavaScript设计模式总结

之前看过《JavaScript设计模式与开发实践》这本书,对书中的设计模式和一些相关案例也有了一定的了解,同时把这些设计模式的应用对应在在一些其他的项目中,进行了一些整理,如下仅供参考: ...

jefferyE
2019/03/26
0
0
《JavaScript设计模式与开发实践》原则篇(2)—— 最少知识原则

最少知识原则(LKP)说的是一个软件实体应当尽可能少地与其他实体发生相互作用。这 里的软件实体是一个广义的概念,不仅包括对象,还包括系统、类、模块、函数、变量等。 单一职责原则指导我们...

嗨呀豆豆呢
2018/12/30
0
0
《JavaScript设计模式与开发实践》最全知识点汇总大全

系列文章: 《JavaScript设计模式与开发实践》基础篇(1)—— this、call 和 apply 《JavaScript设计模式与开发实践》基础篇(2)—— 闭包和高阶函数 《JavaScript设计模式与开发实践》模式...

嗨呀豆豆呢
2019/01/04
0
0
设计模式1——Singleton设计模式

Singleton单例模式是最简单的设计模式,它的主要作用是保证在程序运行生命周期中,使用了单例模式的类只能有一个实例对象存在。单例模式实现了类似C语言中全局变量的功能,单例模式常用于注册...

小米米儿小
2013/12/05
199
1
《JavaScript设计模式与开发实践》模式篇(12)—— 装饰者模式

在传统的面向对象语言中,给对象添加功能常常使用继承的方式,但是继承的方式并不灵活, 还会带来许多问题:一方面会导致超类和子类之间存在强耦合性,当超类改变时,子类也会随之 改变;另一方...

嗨呀豆豆呢
2018/12/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

用 Java 代码,debug 下 JVM 结果亮了!

搭建调试环境 1.下载 CLion 软件 Jetbrains 是一家非常牛逼的公司,咱 Java 里面用到的 IDEA 功能很强大,这公司也为 C/C++ 提供一个 IDE,名叫 CLion,咱需要下载这个 IDE 来调试 JVM 源码。...

Java天天
35分钟前
106
0
php笔记

//多维数组的按照某一个键值判断是否存在某值 public function arrayDot($array, $prepend = '') { $results = []; foreach ($array as $key => $value) { ......

owenzhang24
36分钟前
30
0
Shiro 项目应用 Shiro系列-Shiro简介

简单介绍   对于Shiro来说不仅可以使用到JavaSE的开发中,还可以使用到JavaEE的开发中,Shiro可以完成的工作有。认证、授权、加密、会话管理、与Web的集成、缓存等等操作,shiro 应用实例 ...

FH-Admin
38分钟前
35
0
nacos入门

nocos下载并启动 win下点击startup.cmd即可, 默认端口8848,账户密码均为nacos。 地址栏输入http://localhost:8848/nacos 创建空项目 在空项目中创建子项目(provider)生产者 同理,创建c...

叶湘伦
47分钟前
100
0
Apache Flink 1.10.0 发布 | 云原生生态周报 Vol. 38

作者 | 徐迪、陈俊、敖小剑、宋进超 业界要闻 Apache Flink 1.10.0 发布 作为 Flink 社区迄今为止规模最大的一次版本升级,Flink 1.10 容纳了超过 200 位贡献者对超过 1200 个 issue 的开发实...

阿里巴巴云原生
53分钟前
67
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部