设计模式——观察者模式(Observer)

不可否认,观察者模式是JDK中使用最多的模式之一。通过了解观察者模式,至少我能够更好的梳理一对多的关系,还有对于松耦合的概念。使用观察者模式,至少我们能够切换对象在运行时是否能够被通知(更新消息等),简单来说,通过观察者模式,让我们对消息的更新更及时。

一 初步设计 观察者与气象站

引入一个很常见的案例。现在有一家气象站负责发布气象消息(WeatherData),作为不同的客户,会有不同的对于气象消息的需求,我们也就需要设计一个或者多个面向用户的消息面板获取气象总站的消息,并更新给用户满意的定制数据,用来获取报酬。

First,假设我们有3个信息模块与展示模块。

 

如上图所示,我们需要建立一个应用,利用WeatherData对象取得数据并更新3个布告板。

多的不说,开始敲第一部分代码。

public class WeatherData {
	Board1 board1= new Board1();
	Board2 board2= new Board2();
	Board3 board3= new Board3();

	public WeatherData() {
		// TODO Auto-generated constructor stub
	}
	
	public void weatherChanged(){
		//拟定获取3个数据源的方法
		int temp=getTemp();
		int humidity=getHumidity();
		int pressure=getPressure();
		//更新板子
		board1.updata(temp, humidity, pressure);
		board1.updata(temp, humidity, pressure);
		board1.updata(temp, humidity, pressure);
	}

}

updata()负责3个板子的更新工作。

public class Board1 {
	public Board1() {		
	}
	public void updata(int temp,int humidity,int pressure) {	
	}
}

写的时候感觉没有问题,但是仔细推敲一下,哦,呵呵。我们就盯着weatherChanged()方法看:

1.这种写法类似于针对具体实现编程,不是针对接口。以后增删板子必须修改程序。

2.三个板子类的更新方法入参一样,是不是可以封装成接口?

 

二 引入观察者模式

再次百度一波观察者模式概念:定义了对象的一对多关系,这样一来,当一个对象改变状态时,他的所有依赖者都会收到通知并自动更新。

我们明确目的,需要主题(被观察者,也就是WeatherData)一有变化就能通知到观察者(布告板)。同时为了业务需求,我们必须做到可以动态的删除或者增加观察者。

智者见智,实现观察者模式的方法有很多,我用到的无非是实现Subject和Observer接口。

磨刀不误砍柴工,我还是决定用visio重新画个图梳理一下思路。

然后,又一个蛋疼的概念就来了,松耦合。简单点说,就是观察者和主题不同过于了解彼此,差不多满足业务需求就可以了。

具体点,就是主题只知道观察者实现了Observer接口,其他的可以不管不问,只需要知道谁是观察者,并向其push信息就好了。主题部分的代码不需要修改就可以变更观察者。高端点说,主题可以在运行的时候随意的删增观察者,不需要考虑与观察者的兼容性,本身不会受到观察者变更的影响。这样一来,我们就顺水推舟的建立了具有弹性的OO系统,应对变化能力增强,原因在于对象间的依赖降到了最低。

设计原则:为了交互对象之间的松耦合设计而努力。

 根据上面的流程梳理,我们再次流程图梳理一下我们的布告板工程。

 

根据以上流程图我们开始敲代码把。

1.三个需要实现的接口

public interface Subject {//主题接口
	public void registerObserver(Observer o);//注册
	public void removeObserver(Observer o);//删除
	public void notifyObservers();//主题状态改变,通知所有观察者
}

public interface Observer {//所有观察者必须实现Observer接口
	public void update(float temp, float humidity, float pressure);
}

public interface Display {//布告板需要显示时调用
	public void display();
}

2.看一下我们上面的WeatherData类,我们对他进行优化。

public class WeatherData implements Subject{
	private ArrayList<Observer> observers;
	private float temp;
	private float humidity;
	private float pressure;

	public WeatherData() {
		observers = new ArrayList<Observer>();
	}

	@Override
	public void register(Observer o) {
		observers.add(o);
		
	}

	@Override
	public void remove(Observer o) {
		int i=observers.indexOf(o);
		if (i>=0) {//确认需要删除的观察者在队列中
			observers.remove(i);
		}
	}

	@Override
	public void notifys() {
		//刷新队列中所有已经注册的观察者
		for (int i = 0; i < observers.size(); i++) {
			observers.get(i).update(temp, humidity, pressure);
		}
	}
	
	public void weatherChanged() {
		//气象站得知消息更新时调用,更新观察者(布告板)
		notifys();
	}
	
	//主动刷新气象信息
	public void setWeather(float temperature, float humidity, float pressure) {
		this.temp = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		weatherChanged();
	}

	//单独获取气象信息的3个get方法
	public float getTemperature() {
		return temp;
	}
	
	public float getHumidity() {
		return humidity;
	}
	
	public float getPressure() {
		return pressure;
	}

}

3.既然WeatherData已经完工,开始设计我们的布告板1。

public class Board1 implements Observer, Display{
	private float temp;
	private float humidity;
	private Subject weatherData;
	
	public Board1(Subject weatherData) {
		this.weatherData = weatherData;
		weatherData.register(this);
	}
	
	public void update(float temperature, float humidity, float pressure) {
		this.temp = temperature;
		this.humidity = humidity;
		display();//当气象信息改变立即刷新布告板
	}
	
	public void display() {
		System.out.println("Current conditions: " + temperature );
	}
}

其他的布告板作为一个观察者,其实都是大同小异,都去继承Observcer和Display接口并一一实现就好。

我们这套拥有观察者特质的气象系统已经建造完毕了,我们可以用一个测试类测试一下:

public class WeatherTest {

	public static void main(String[] args) {
		WeatherData weatherData = new WeatherData();
	
		CurrentConditionsDisplay currentDisplay = new Board1 (weatherData);

		weatherData.setWeather(80, 65, 30.4f);
		weatherData.setWeather(82, 70, 29.2f);
		weatherData.setWeather(78, 90, 29.2f);
	}
}

e嗯哼,结果符合预期。

 

三 JAVA内置的观察者模式。

如果你真的跟着这篇博客从头看到这里。。。我其实会有点无语的。。。因为,因为,其实JAVA里面有内置的观察者模式,= =看来你真的是对观察者模式一无所知。java.util内包含Observerble类和Observer接口,甚至比我们前面的subject更好用哟~

好吧,我们再次对系统进行修改,允许我再次插个图。

差异性。

1.我们发现最大差异就是主题WeatherData只需要继承系统自带的Observable类。观察者通过Observer接口实现不变,改变的是通过addObserver()和deleteObserver方法进行注册和解绑。

2.主题发送通知,需要先调用setChange()方法,先声明状态的改变。然后调用notify方法进行刷新观察者状态。基本上与我们上面大同小异。

public class WeatherData extends Observable{
	private float temperature;
	private float humidity;
	private float pressure;
	
	public WeatherData() {
		// TODO Auto-generated constructor stub
	}
	
	public void measurementsChanged() {
		setChanged();
		
		//观察者需要改变什么数据,是需要观察者自己到主题那里去pull.
		notifyObservers();
		
	        /*由主题主动的PUSH需要改变的数据对象给观察者
		Object arg 其实就是 Observer接口中的update(Observable o, Object arg)方法中的第二个参数 */
		//notifyObservers(temperature);
	}
	
	public void setMeasurements(float temperature, float humidity, float pressure) {
		this.temperature = temperature;
		this.humidity = humidity;
		this.pressure = pressure;
		measurementsChanged();
	}
	
	public float getTemperature() {
		return temperature;
	}
	
	public float getHumidity() {
		return humidity;
	}
	
	public float getPressure() {
		return pressure;
	}
}

这里我们需要需要注意Observable中的2个方法。百度上资料很多,我简单说明一下。

1. setChanged()    

先看下源码中的注解

    /**
     * Marks this <tt>Observable</tt> object as having been changed; the
     * <tt>hasChanged</tt> method will now return <tt>true</tt>.
     */
    protected synchronized void setChanged() {
        changed = true;
    }

意思就是讲继承Observable的主题类(可观察者)状态改变为已更改。在notifyObservers()之前,一定要先调用这个方法来指示已经改变的主题状态。

同时,还有cleraChanged()方法讲change状态设置为false,hasChanged()返回当前change的状态。

2. notifyObserver()&notifyObserver(Object arg)

带参数的notifyObservers(Object arg):这个参数Object arg 其实就是 Observer接口中的update(Observable o, Object arg)方法中的第二个参数 。就是通知观察者所改变的数据对象。简单理解就是由主题主动的PUSH需要改变的数据对象给观察者。

不带参数的方法,传递一个null数据对象给观察者,需要观察者主动到主题pull数据。

其他的不在复述。

下面看一下我们的布告板(观察者)有上面改变,直接上代码。

package ObserverTest;

import java.util.Observable;
import java.util.Observer;
	
public class CurrentConditionsDisplay implements Observer, DisplayElement {
	Observable observable;
	private float temperature;
	private float humidity;
	
	public CurrentConditionsDisplay(Observable observable) {
		//构造方法需要引入一个主题类(继承Observable)
		this.observable = observable;
		//加入主题注册队列
		observable.addObserver(this);
	}
	
	public void update(Observable obs, Object arg) {
		if (obs instanceof WeatherData) {//判断主题是否对应 、
			//默认为pull方式获取更新数据,这样适应性更强
			WeatherData weatherData = (WeatherData)obs;
			this.temperature = weatherData.getTemperature();
			this.humidity = weatherData.getHumidity();
			//更新面板数据
			display();
		}
	}
	
	public void display() {
		System.out.println("Current conditions: " + temperature 
			+ "F degrees and " + humidity + "% humidity");
	}
}

OK,搞到这一步,基本上观察者模式已经梳理完了,通过这篇博客的整理,我也对观察者模式的认知提高了一个层次。

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
书名: 设计模式可复用面向对象软件的基础 英文原书名: Design Patterns:Elements of Reusable Object-Oriented software 作者: Erich Gamma 等 译者: 李英军 马晓星 蔡敏 刘建中 书号: 7-111-07575-7 页码: 254 定价: ¥35.00 会员价: ¥31.50 币值: 315 出版日期: 2000-9-1 本书设计实例从面向对象的设计中精选出23个设计模式,总结了面向对象设计中最有价值的经验,并且用简洁可复用的形式表达出来。本书分类描述了一组设计良好,表达清楚的软件设计模式,这些模式在实用环境下有特别有用。 -------- 目 录 序言 前言 读者指南 第1章 引言 1 1.1 什么是设计模式 2 1.2 Smalltalk MVC中的设计模式 3 1.3 描述设计模式 4 1.4 设计模式的编目 5 1.5 组织编目 7 1.6 设计模式怎样解决设计问题 8 1.6.1 寻找合适的对象 8 1.6.2 决定对象的粒度 9 1.6.3 指定对象接口 9 1.6.4 描述对象的实现 10 1.6.5 运用复用机制 13 1.6.6 关联运行时刻和编译时刻的 结构 15 1.6.7 设计应支持变化 16 1.7 怎样选择设计模式 19 1.8 怎样使用设计模式 20 第2章 实例研究:设计一个文档编 辑器 22 2.1 设计问题 23 2.2 文档结构 23 2.2.1 递归组合 24 2.2.2 图元 25 2.2.3 组合模式 27 2.3 格式化 27 2.3.1 封装格式化算法 27 2.3.2 Compositor和Composition 27 2.3.3 策略模式 29 2.4 修饰用户界面 29 2.4.1 透明围栏 29 2.4.2 Monoglyph 30 2.4.3 Decorator 模式 32 2.5 支持多种视感标准 32 2.5.1 对象创建的抽象 32 2.5.2 工厂类和产品类 33 2.5.3 Abstract Factory模式 35 2.6 支持多种窗口系统 35 2.6.1 我们是否可以使用Abstract Factory 模式 35 2.6.2 封装实现依赖关系 35 2.6.3 Window和WindowImp 37 2.6.4 Bridge 模式 40 2.7 用户操作 40 2.7.1 封装一个请求 41 2.7.2 Command 类及其子类 41 2.7.3 撤消和重做 42 2.7.4 命令历史记录 42 2.7.5 Command 模式 44 2.8 拼写检查和断字处理 44 2.8.1 访问分散的信息 44 2.8.2 封装访问和遍历 45 2.8.3 Iterator类及其子类 46 2.8.4 Iterator模式 48 2.8.5 遍历和遍历过程中的动作 48 2.8.6 封装分析 48 2.8.7 Visitor 类及其子类 51 2.8.8 Visitor 模式 52 2.9 小结 53 第3章 创建型模式 54 3.1 Abstract Factory(抽象工厂)— 对象创建型模式 57 3.2 Builder(生成器)—对象创建型 模式 63 3.3 Factory Method(工厂方法)— 对象创建型模式 70 3.4 Prototype(原型)—对象创建型 模式 87 3.5 Singleton(单件)—对象创建型 模式 84 3.6 创建型模式的讨论 89 第4章 结构型模式 91 4.1 Adapter(适配器)—类对象结构型 模式 92 4.2 Bridge(桥接)—对象结构型 模式 100 4.3 Composite(组成)—对象结构型 模式 107 4.4 Decorator(装饰)—对象结构型 模式 115 4.5 FACADE(外观)—对象结构型 模式 121 4.6 Flyweight(享元)—对象结构型 模式 128 4.7 Proxy(代理)—对象结构型 模式 137 4.8 结构型模式的讨论 144 4.8.1 Adapter与Bridge 144 4.8.2 Composite、Decorator与Proxy 145 第5章 行为模式 147 5.1 CHAIN OF RESPONSIBIL ITY(职责链) —对象行为型模式 147 5.2 COMMAND(命令)—对象行为型 模式 154 5.3 INTERPRETER(解释器)—类行为型 模式 162 5.4 ITERATOR(迭代器)—对象行为型 模式 171 5.5 MEDIATOR(中介者)—对象行为型 模式 181 5.6 MEMENTO(备忘录)—对象行为型 模式 188 5.7 OBSERVER(观察者)—对象行为型 模式 194 5.8 STATE(状态)—对象行为型模式 201 5.9 STRATEGY(策略)—对象行为型 模式 208 5.10 TEMPLATE METHOD(模板方法) —类行为型模式 214 5.11 VISITOR(访问者)—对象行为型 模式 218 5.12 行为模式的讨论 228 5.12 1 封装变化 228 5.12.2 对象作为参数 228 5.12.3 通信应该被封装还是被分布 229 5.12.4 对发送者和接收者解耦 229 5.12.5 总结 231 第6章 结论 232 6.1 设计模式将带来什么 232 6.2 一套通用的设计词汇 232 6.3 书写文档和学习的辅助手段 232 6.4 现有方法的一种补充 233 6.5 重构的目标 233 6.6 本书简史 234 6.7 模式界 235 6.8 Alexander 的模式语言 235 6.9 软件中的模式 236 6.10 邀请参与 237 6.11 临别感想 237 附录A 词汇表 238 附录B 图示符号指南 241 附录C 基本类 244 参考文献 249
创建型: 1. 单件模式(Singleton Pattern) 2. 抽象工厂(Abstract Factory) 3. 建造者模式(Builder) 4. 工厂方法模式(Factory Method) 5. 原型模式(Prototype) 结构型: 6. 适配器模式(Adapter Pattern) 7. 桥接模式(Bridge Pattern) 8. 装饰模式(Decorator Pattern) 9. 组合模式(Composite Pattern) 10. 外观模式(Facade Pattern) 11. 享元模式(Flyweight Pattern) 12. 代理模式(Proxy Pattern) 13. 模板方法(Template Method) 14. 命令模式(Command Pattern) 15. 迭代器模式(Iterator Pattern) 行为型: 16. 观察者模式(Observer Pattern) 17. 解释器模式(Interpreter Pattern) 18. 中介者模式(Mediator Pattern) 19. 职责链模式(Chain of Responsibility Pattern) 20. 备忘录模式(Memento Pattern) 21. 策略模式(Strategy Pattern) 22. 访问者模式(Visitor Pattern) 23. 状态模式(State Pattern) 工程结构 ├─01.Singleton │ ├─html │ └─MySingleton │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─02.ChainOfResponsibility │ ├─html │ ├─My2ChainOfResponsibility │ │ ├─bin │ │ │ └─Debug │ │ ├─obj │ │ │ └─Debug │ │ │ └─TempPE │ │ └─Properties │ └─MyChainOfResponsibility │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ ├─Refactor │ │ └─TempPE │ └─Properties ├─03.FactoryMethodMode │ ├─FactoryMethodMode │ │ ├─bin │ │ │ └─Debug │ │ ├─obj │ │ │ └─Debug │ │ │ └─TempPE │ │ └─Properties │ └─html ├─04.AbstractFactory │ ├─04.1.SimpleFactory │ │ ├─html │ │ └─SimpleFactory │ │ ├─bin │ │ │ └─Debug │ │ ├─obj │ │ │ └─Debug │ │ │ └─TempPE │ │ └─Properties │ ├─AbstractFactory │ │ ├─bin │ │ │ └─Debug │ │ ├─obj │ │ │ └─Debug │ │ │ └─TempPE │ │ └─Properties │ └─html ├─05.BuilderPattern │ ├─html │ └─MyBuilderPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─06.PrototypePattern │ ├─html │ │ └─C#设计模式(6)——原型模式(Prototype Patt O技术博客_files │ └─PrototypePattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─07.AdapterPattern │ ├─html │ └─MyAdapterPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─08.BridgePattern │ ├─html │ └─MyBridgePattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─09.DecoratorPattern │ ├─html │ └─MyDecoratorPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─10.CompositePattern │ ├─html │ └─MyCompositePattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─11.FacadePattern │ ├─html │ └─MyFacadePattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─12.FlyweightPattern │ ├─html │ └─MyFlyweightPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─13.ProxyPattern │ ├─html │ └─MyProxyPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─14.TemplateMethod │ ├─html │ └─MyTemplateMethod │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─15.VisitorPattern │ ├─html │ └─MyVisitorPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─16.StrategyPattern │ ├─html │ └─MyStrategyPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─17.StatePattern │ ├─html │ └─StatePattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─18.MementoPattern │ ├─html │ └─MementoPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─19.MediatorPattern │ ├─html │ └─MediatorPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─20.OberverPattern │ ├─CatOberverPattern │ │ ├─bin │ │ │ └─Debug │ │ ├─obj │ │ │ └─Debug │ │ │ └─TempPE │ │ └─Properties │ ├─html │ └─MyOberverPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─21.IteratorPattern │ ├─html │ └─IteratorPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties ├─22.InterpreterPattern │ ├─html │ └─MyInterpreterPattern │ ├─bin │ │ └─Debug │ ├─obj │ │ └─Debug │ │ └─TempPE │ └─Properties └─23.CommandPattern ├─html └─MyCommandPattern ├─bin │ └─Debug ├─obj │ └─Debug │ └─TempPE └─Properties

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值