认识观察者模式

我们用报纸和杂志的订阅为例来介绍:

  1. 报社的业务就是出版报纸。

  2. 向某家报社订阅报纸,只要他们有新报纸出版,就会给你送来。只要你是他们的订户,你就会一直收到新报纸。

  3. 当你不想再看报纸的时候,取消订阅,他们就不会再送新报纸来。

  4. 只要报社还在运营,就会一直有人(或单位)向他们订阅报纸或取消订阅报纸。

出版者+订阅者=观察者模式,如果了解报纸的订阅,就知道观察者模式是怎么回事,只是名称不太一样:出版者改为“主题”(Subject),订阅者改为“观察者”(Observer)。

定义观察者模式

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

类图:

案例:气象观测站

项目背景:

此系统中的三个部分是气象站(获取实际气象数据的物理装置)、WeatherData对象(追踪来自气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看)。

WeatherData对象知道如何跟物理气象站联系,以取得更新的数据。WeatherData对象会随即更新三个布告板的显示:目前状况(温度、湿度、气压)、气象统计和天气预报。

目前已知WeatherData的设计如下:

public class WeatherData{
public float getTemperature();
public float getHumidity();
public float getPressure(); // 一旦气象测量更新,此方法会被调用
public void measurementsChanged(){
// 你的代码加在这里
}
}

首先我们必须搞懂要做什么。那么,我们目前知道些什么呢?

  1. WeatherData类具有getter方法,可以取得三个测量值:温度、湿度与气压。

  2. 当新的测量数据更新时,measurementsChanged()方法就会被调用。

  3. 我们需要实现三个使用天气数据的布告板:“目前状况”布告、“气象统计”布告、“天气预报”布告。一旦WeatherData有新的测量,这些布告必须马上更新。

  4. 此系统必须可扩展,可定制新的布告板。

先看一个示范

public class WeatherData{
// 实例变量声明 public void measurementsChanged(){ // 获取测量值
float temp = getTemperature();
float humidity = getHumidity();
float pressure = getPressure(); // 更新布告板
currentConditionsDisplay.update(temp, humidity, pressure);
statisticsDisplay.update(temp, humidity, pressure);
forecastDisplay.update(temp, humidity, pressure);
} // 这里是其他WeatherData方法
}

思考:上面的示范有什么问题?

回想第一章的概念和原则。

使用观察者模式重新设计气象站

实现气象站

public interface Subject{
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
} public interface Observer{
void update(float temperature, float humidity, float pressure);
} public interface DisplayElement{
void display();
} public class WeatherData: Subject{
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure; public WeatherData(){
observers = new List<Observer>();
} public void registerObserver(Observer o){
observers.Add(o);
} public void removeObserver(Observer o){
int i = observers.IndexOf(o);
if(i >= ){
observers.RemoveAt(i);
}
} public void notifyObservers(){
foreach(var observer in observers){
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();
} // WeatherData的其他方法
}

建立布告板

public class CurrentConditionsDisplay: Observer, DisplayElement{
private float temperature;
private float humidity;
private Subject weatherData; public CurrentConditionsDisplay(Subject 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 conditions: {0}F degrees and {1}% humidity", temperature, humidity);
}
}

启动气象站

public class Program{
public static void Main(string[] args){
WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
ForecastDisplay = forecastDisplay = new ForecastDisplay(weatherData); weatherData.setMeasurements(, , 30.4F);
weatherData.setMeasurements(, , 29.2F);
weatherData.setMeasurements(, , 29.2F);
}
}

运行结果:

C#的Event实现观察者模式

.net中的Event模型是一种典型的观察者模型,在.net出身之后被大量应用在了代码当中,我们看事件模型如何在此种场景下使用。

public delegate void DisplayElementHandler(WeatherDataEventArgs args);

public interface DisplayElement
{
void OnDisplay(WeatherDataEventArgs args);
} /// <summary>
/// 主题
/// </summary>
public class WeatherData
{
public event DisplayElementHandler DisplayEvent; private float temperature;
private float humidity;
private float pressure; public void SetMeasurements(float temperature, float humidity, float pressure)
{
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure; if (DisplayEvent != null)
{
DisplayEvent(new WeatherDataEventArgs { Temperature = this.temperature, Humidity = this.humidity, Pressure = this.pressure });
}
} // WeatherData的其他方法
} /// <summary>
/// 订阅者
/// </summary>
public class CurrentConditionsDisplay : DisplayElement
{
public void OnDisplay(WeatherDataEventArgs args)
{
Console.WriteLine("Current conditions: {0}F degrees and {1}% humidity", args.Temperature, args.Humidity);
}
} public class App
{
public static void Run()
{
WeatherData weatherData = new WeatherData(); CurrentConditionsDisplay currentConditions = new CurrentConditionsDisplay(); weatherData.DisplayEvent += new DisplayElementHandler(currentConditions.OnDisplay); weatherData.SetMeasurements(, , 30.4F);
weatherData.SetMeasurements(, , 29.2F);
weatherData.SetMeasurements(, , 29.2F);
}
} public class WeatherDataEventArgs
{
public float Temperature { get; set; }
public float Humidity { get; set; }
public float Pressure { get; set; }
}

观察者模式总结

观察者模式的主要优点如下:

  • 观察者模式可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色。

  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次。

  • 观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多系统设计的难度。

  • 观察者模式满足 “开闭原则” 的要求,增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便。

观察者模式的主要缺点如下:

  • 如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。

  • 如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。

  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

适用场景:

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中使它们可以各自独立地改变和复用。

  • 一个对象的改变将导致一个或多个其他对象也发生改变,而并不知道具体有多少对象将发生改变,也不知道这些对象是谁。

  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。

HeadFirst学习笔记-2.观察者(Observer)模式的更多相关文章

  1. Java 实现观察者(Observer)模式

    1. Java自带的实现 类图 /** * 观察目标 继承自 java.util.Observable * @author stone * */ public class UpdateObservab ...

  2. jsp学习笔记:mvc开发模式

    jsp学习笔记:mvc开发模式2017-10-12 22:17:33 model(javabe)与view层交互 view(视图层,html.jsp) controller(控制层,处理用户提交的信息 ...

  3. Java设计模式学习笔记(二) 简单工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 正文开始... 1. 简介 简单工厂模式不属于GoF23中设计模式之一,但在软件开发中应用也较为 ...

  4. Java设计模式学习笔记(三) 工厂方法模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 简介 上一篇博客介绍了简单工厂模式,简单工厂模式存在一个很严重的问题: 就是当系统需要引入 ...

  5. Java设计模式学习笔记(四) 抽象工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...

  6. 设计模式C++描述----04.观察者(Observer)模式

    一. 概述 Observer 模式要解决的问题为:建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变. Sbuject 相当于 ...

  7. 观察者(Observer)模式

    观察者模式又叫做发布-订阅模式(Publish.Subscribe)模式.模型-视图模式(Model/View)模式.源-监听器模式(Source/Listener)模式或从属者(Dependents ...

  8. Java设计模式之从[星际争霸的兵种升级]分析观察者(Observer)模式

    观察者模式定义对象的一种一对多的依赖关系.当一个对象的状态发生改变时.全部依赖于它的对象都会得到通知并被自己主动更新. 一个简单的样例是.在星际争霸的虫族中有一个0基础单位叫做跳狗(Zergling) ...

  9. 《Head First 设计模式》学习笔记——观察者模式 + 装饰者模式

    装饰者模式是JDK中还有一个使用较多的设计模式,上一个是观察者模式(在Swing中大量使用),业内好的API设计无一离不开常见的设计模式,通常我们所说要阅读源代码,也是为了学习大牛们的设计思路.--- ...

随机推荐

  1. Neovim中NERDTree等多处cursorline不高亮

    标题表达的不是很清楚,看下图把 解决方法 添加下面内容到init.vim " 针对NERDTree " https://github.com/scrooloose/nerdtree ...

  2. 把xml数据直接插入到sqlserver数据库

    存储过程: ALTER proc [ali].[ins_冻结金额表] @xmldoc varchar(max), ) as declare @idoc int exec sp_xml_prepared ...

  3. pt-show-grants的用法

    pt-show-grants的用法 1.先查找所有用户和Host 2.然后逐个执行show grants pt-show-grants的功能是格式化打印输出MySQL上的赋权,以便你可以有效地复制.比 ...

  4. cucumber测试项目报错

    [INFO] Scanning for projects... [WARNING] [WARNING] Some problems were encountered while building th ...

  5. java 源码编译

    Java语言的“编译期”其实是一段“不确定”的操作过程,因为它可能是指一个前端编译器(叫“编译器的前段”更准确)——把*.java文件转变成*.class文件的过程:也可能是虚拟机的后端运行期编译器( ...

  6. Hadoop高可用集群

    1.简介 若HDFS集群中只配置了一个NameNode,那么当该NameNode所在的节点宕机,则整个HDFS就不能进行文件的上传和下载. 若YARN集群中只配置了一个ResourceManager, ...

  7. sql server登录名、服务器角色、数据库用户、数据库角色、架构区别联系

    原创链接:https://www.cnblogs.com/lxf1117/p/6762315.html sql server登录名.服务器角色.数据库用户.数据库角色.架构区别联系 1.一个数据库用户 ...

  8. 20155324《网络对抗》Exp07 网络欺诈防范

    20155324<网络对抗>Exp07 网络欺诈防范 实践内容 简单应用SET工具建立冒名网站 ettercap DNS spoof 结合应用两种技术,用DNS spoof引导特定访问到冒 ...

  9. UE4物理笔记

    基本 物理资源随骨骼创建,可添加到骨骼网格上. 物理材质可添加到材质或组件或物理资源上. 通过配置PrimitiveComponent组件的Collision Presets值,可实现自定义的碰撞忽略 ...

  10. Django 2.0 官方文档翻译

    from django.contrib import admin from django.urls import include, path urlpatterns = [ path('polls/' ...