看了好几篇文章,最终还是觉得《Head First 设计模式》举得例子比较符合观察者模式。

观察者模式概述:

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

观察者模式所涉及的角色有:

  ●  抽象主题(Subject)角色:

      抽象主题角色把所有对观察者对象的引用保存在一个聚集(比如ArrayList对象)里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,抽象主题角色又叫做抽象被观察者(Observable)角色。

  ●  具体主题(ConcreteSubject)角色:

      将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者(Concrete Observable)角色。

  ●  抽象观察者(Observer)角色:观察者。

      为所有的具体观察者定义一个接口,在得到主题的通知时更新自己,这个接口叫做更新接口。

  ●  具体观察者(ConcreteObserver)角色:

      存储与主题的状态自恰的状态。具体观察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态 像协调。如果需要,具体观察者角色可以保持一个指向具体主题对象的引用。

观察者模式特点:

  个人认为,观察者注册时应该持有主体对象,以便注销。

前言:

这一节开始学习观察者模式,开始讲之前会先像第一节那样通过一个应用场景来引入该模式。具体场景为:气象站提供了一个WeatherData对象,该对象可以追踪获取天气的温度、气压、湿度信息,WeatherData对象会随即更新三个布告板的显示:目前状况(温度、湿度、气压)、气象统计和天气预报。

1.    基本需求:利用WeatherData对象获取数据、并更新三个布告板:目前状况、气象统计和天气预报

WeatherData类图如下:

说明:

GetTemperature()、GetHumidity()、GetPressure()分别用来获取天气温度、湿度气压,MeasurementsChanged()当气象测量更新此方法会被调用。

分析:

  1. 可以通过WeatherData的Getter方法获取三个测量值:温度、湿度、气压
  2. 气象测量更新时会调用MeasurementsChanged()方法
  3. 需要实现三个天气数据布告板:“目前状况”、“气象统计”、“天气预报”,一旦WeatherData测到新值,这些布告也马上更新
  4. 此系统可扩展,可以随意的添加或者删除任何布告板。

实现:

public class WeatherData
{
public void MeasurementsChanged()
{
float temp = GetTemperature();
float humidity = GetHumidity();
float pressure = GetPressure();
currentConditionDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
}
//其他方法
}

反思:

我们的这种实现有何不妥?

结合我们第一节中的一些面向对象的原则,这种实现方式会有如下问题:

l  针对具体实现编程,后续新增或者删除布告板必须修改程序

l  未将改变的地方封装起来

这种实现方式与面向对象的一些基本原则是相违背的,目前暂时是实现了用户的需求,但是在后续不具备可扩展行。

2.    引入观察者模式

2.1 出版者+订阅者=观察者模式

出版者:就相当于“主题”(Subject),订阅者相当于“观察者”(Observer)

当出版者(主题)发行新的报纸的时候,所有的观察者(订阅者)就可以收到最新的报纸,同时,当新的观察者(订阅者)加入时,也可以收到最新的报纸,当观察者(订阅者)退订报纸后,就再也收不到新的报纸。

2.2 定义观察者模式

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

主题和观察者定义一对多的关系。观察者依赖于此主题,只要主题状态一有变化,观察者就会被通知,根据通知的风格,观察者可能因此新值而更新。

观察者模式:类图

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

松耦合的威力

l  当两个对象之间松耦合,它们依然可以交互,但是不清楚彼此的细节。

l  观察者模式提供了一种对象设计,让主题和观察者之间松耦合。

说明:

  1. 主题只知道观察者实现了某个接口(IObserver接口),不需要知道观察者是谁,或其他细节。
  2. 任何时候都可以增加或者删除的观察者,主题唯一依赖的是一个实现了IObserver接口的对象列表。
  3. 新的类型观察者出现时,主题代码不需要修改,只需要在新类型里实现观察者接口,然后注册为观察者即可。
  4. 可以独立的复用主题或观察者,因为二者松耦合。
  5. 改变主题或者观察者,并不会影响另一方。因为二者松耦合。

松耦合的设计之所以能让我们建立有弹性的OO系统,能够应对变化,是因为对象之间的相互依赖讲到了最低。

3.    利用观察者模式设计并实现气象站

3.1 设计气象站

类图:

3.2具体实现

3.2.1主题、观察者、显示接口
 
/// Description:对象、观察者、显示接口
/// </summary>
public interface ISubject
{
void RegisterObserver(IObserver o);//注册观察者
void RemoveObserver(IObserver o);//删除观察者
void NotifyObervers();//通知观察者
} public interface IObserver
{
void Update(float temp, float humidity, float pressure);
} public interface IDisplayElement
{
void Display();
}
 
3.2.2 WeatherData类:注册、删除、通知观察者
 
    /// Description:WeatherData 注册、删除、通知观察者
/// </summary>
public class WeatherData:ISubject
{
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure; public WeatherData()
{
observers = new ArrayList();//初始化obervers,用来存储注册的观察者
} /// <summary>
/// 注册观察者
/// </summary>
/// <param name="o"></param>
public void RegisterObserver(IObserver o)
{
observers.Add(o);
} /// <summary>
/// 删除观察者
/// </summary>
/// <param name="o"></param>
public void RemoveObserver(IObserver o)
{
int i = observers.IndexOf(o);
if (i >= 0)
observers.Remove(o);
} /// <summary>
/// 通知观察者
/// </summary>
public void NotifyObervers()
{
foreach (IObserver o in observers)
{
o.Update(temperature, humidity, pressure);
}
} /// <summary>
/// 当从气象站得到更新观测值时,通知观察者
/// </summary>
public void MeasurementsChanged()
{
NotifyObervers();
} /// <summary>
///
/// </summary>
/// <param name="temperature"></param>
/// <param name="humidity"></param>
/// <param name="pressure"></param>
public void SetMeasurements(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
MeasurementsChanged();
}
}
 
3.2.3 布告板类,实现了IObserver、IDisplayElement接口

 

 
    /// Description:创建布告板
/// </summary>
public class CurrentConditionsDisplay:IObserver,IDisplayElement
{
private float temperature;
private float humidity;
private ISubject weatherData; public CurrentConditionsDisplay(ISubject weatherData)
{
this.weatherData = weatherData;
weatherData.RegisterObserver(this);
} public void Update(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
Display();
}
public void Display()
{
Console.WriteLine("Current coditions: " + temperature + "F degress and " + humidity + "% humidity");
}
}
 
3.2.4 测试
WeatherStation.WeatherData weatherData = new WeatherStation.WeatherData();
WeatherStation.CurrentConditionsDisplay currentDisplay = new WeatherStation.CurrentConditionsDisplay(weatherData);
weatherData.SetMeasurements(10, 20, 30);

结果如下:

4.    Java内置的观察者模式

Java内置的观察者模式,许多功能都已经事先准备好了,甚至可以用推(push)或拉(pull)的方式传送数据。

使用java内置观察者模式实现气象站的OO设计类图,如下:

Java内置观察者模式与我们在3小节中明显的差异是WeatherData继承自Observable类,并集成了一些增加、删除、通知观察者的方法。

l  将对象变成观察者

首先还是要实现Observer(观察者)接口,其次调用Observable对象的addObserver()方法即可。

l  可观察者(主题)送出通知

  1. 先调用setChanged()方法,标记状态已经改变的事实。
  2. 调用notifyObservers()方法(该方法有2个,任意一个皆可):notifyObservers()或notifyObservers(Object arg)

l  观察者接收通知

观察者实现了Observer的接口,方法签名如下:

update(Observable o,Object arg)

第一个参数为主题本身,让观察者知道是哪个主题通知它的

第二个参数是传入notifyObservers()的数据对象

如果想用“推”的方式将数据给观察者,则可以把数据当做数据对象的方式传给notifyObservers(arg)

如果想用“拉”的方式将数据给观察者,则需要在update()中,通过WeatherData的getTemperature()等方法获取对应的气象值。

缺陷:

Java内置的观察者模式中Observable是一个类,你必须设计一个类去继承它。如果某类相同时具有Observable类和另一个超类的行为,就无法实现,因为java不支持多继承。

同时也限制了Observable的复用能力。

同时,Observable API将setChanged()方法保护了起来,除非继承自Observable类,否则无法创建Observable实例组合到自己的对象中,也违背了面向对象设计的第二个原则:多用组合,少用继承。

5.    总结

l  OO原则:

封装变化

多用组合,少用继承

针对接口编程,不针对实现编程

对交互对象之间的松耦合设计而努力(新的OO原则,松耦合的设计更有弹性,更能应对变化)

l  OO模式:

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

参看以及内容来源:

http://www.cnblogs.com/java-my-life/archive/2012/05/16/2502279.html

http://www.cnblogs.com/Olive116/p/5270948.html

java_设计模式_观察者模式_Observer Pattern(2016-07-27)的更多相关文章

  1. java_设计模式_适配器模式_Adapter Pattern(2016-08-09)

    概念 将一个接口转换成客户希望的另外一个接口.(该模式使得原本不兼容的类可以一起工作). UML图 适配器模式有类的适配器模式和对象的适配器模式两种不同的形式. (1)对象的适配器模式结构图 (2)类 ...

  2. java_设计模式_单例模式_Singleton Pattern(2016-08-04)

    概念: 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 适用场景: 在计算机系统中,线程池.缓存.日志对象.对话框.打印机.显卡的驱动程序对象常被设计成单例.这些应用都或多或 ...

  3. 乐在其中设计模式(C#) - 观察者模式(Observer Pattern)

    原文:乐在其中设计模式(C#) - 观察者模式(Observer Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 观察者模式(Observer Pattern) 作者:weba ...

  4. 二十四种设计模式:观察者模式(Observer Pattern)

    观察者模式(Observer Pattern) 介绍定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新. 示例有一个Message实体类,某些对象 ...

  5. java_设计模式_装饰者模式_Decorator Pattern(2016-07-28)

    装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰模式的结构 装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任.换言之,客户 ...

  6. java_设计模式_组合模式_Composite Pattern(2016-08-12)

    概念: 组合模式(Composite Pattern)将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性. 有时候又叫做部分-整体模式,它使我们树 ...

  7. java_设计模式_模板方法模式_Template Method Pattern(2016-08-11)

    定义: 定义一个操作中算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变算法的结构即可重定义该算法中的某些特定步骤.这里的算法的结构,可以理解为你根据需求设计出来的业务流程.特定的步骤就是指那些 ...

  8. java_设计模式_命令模式_Command Pattern(2016-08-09)

    理解还不到位,先窜出来.等过一阵子再看,再理解. 定义:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能. 类型:行为类模式 类 ...

  9. java_设计模式_工厂模式_Factory Pattern(2016-08-04)

    工厂模式主要是为创建对象提供了接口.工厂模式按照<Java与模式>中的提法分为三类: (1)简单工厂(Simple Factory)模式,又称静态工厂方法模式(Static Factory ...

随机推荐

  1. soundtouch源码分析__based on csdn :

    1. soundtouch介绍和相关资源 The SoundTouch Library Copyright © Olli Parviainen 2001-2014 SoundTouch is an o ...

  2. Linux学习笔记27——共享内存

    一 共享内存 共享内存是由IPC为进程创建的一个特殊的地址范围,它将出现在该进程的地址空间中.其他进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址.如果某个进程向 ...

  3. Linux学习笔记20——第一个多线程程序

    一 什么是线程 线程:是一个进程内部的一个控制序列. 二 使用POSIX的注意点 1 为了使用线程函数库,必须定义宏_REENTRANT,通过定义_REENTRANT来告诉编译器我们需要可重入功能,可 ...

  4. HDOJ/HDU 2566 统计硬币(公式~遍历~)

    Problem Description 假设一堆由1分.2分.5分组成的n个硬币总面值为m分,求一共有多少种可能的组合方式(某种面值的硬币可以数量可以为0). Input 输入数据第一行有一个正整数T ...

  5. ipython notebook使用教程

    在一次师兄(师兄博客地址)的例会汇报中,介绍了ipython notebook,当时觉得很酷炫,渐渐自己使用的时候才发现真的很强大.抽空整理下,找了些资料进行补充,并挨个进行了实现,留个笔记,也欢迎喜 ...

  6. 《Linear Algebra and Its Applications》-chaper3-行列式-克拉默法则

    计算线性方程组唯一解的克拉默法则:

  7. kickstartInstalls

  8. Call-time pass-by-reference has been deprecated

    Warning: Call-time pass-by-reference has been deprecated解决方法 第一种方法: 修改php.ini就可以了. 1. 在PHP.ini中搜索关键字 ...

  9. JAVA学习.java.sql.date 与java.util.date以及gettime()方法的分析

    java.sql.Date 是针对SQL语句使用的,它只包含日期而没有时间部分. java.util.Date 就是在除了SQL语句的情况下面使用. 它都有getTime方法返回毫秒数,返回的是自19 ...

  10. (使用步骤)ThinkPHP3.1.2中如何配置Ckeditor_4.1.1和Ckfindtor(转)

    ThinkPHP3.1.2中如何配置Ckeditor_4.1.1和Ckfindtor  一.下载Ckeditor和Ckfinder Ckeditor官网 http://ckeditor.com/dow ...