设计模式(20)行为型模式 - 观察者模式

前言

温故而知新

先复习前面学习的行为型模式:

  • 模板方法模式:在父类定义算法、流程的骨架,一些特定的步骤延迟到子类实现,用户可以复用父类模板(请客流程:点单 -》吃 -》买单,具体吃什么由子类决定)
  • 命令模式:将命令封装成对象,聚合命令执行者,命令请求者仅需调用命令对象即可完成命令执行,将命令请求者与命令执行者完全解耦(遥控器的按钮是一个个命令对象,遥控器按下按钮即可实现开关灯)
  • 访问者模式:将施加于某个对象结构的元素对象上的操作隔离,封装成访问者,访问者改变也不会修改对象结构(商城购物,购物车存储着各种商品对象,顾客可以查看购物车里商品的质量,收银员可以查看购物车里商品的价格)
  • 迭代器模式:将聚合对象中存储对象功能和遍历对象功能分隔,遍历对象功能封装成一个迭代器,可以对不同的聚合对象使用对应的迭代器或更换不同的迭代器(Linux目录以迭代器的方式描述层级目录)

接下来,完成观察者模式的学习


现实中的问题

我们知道行为型模式是描述对象或类间的协作行为

在现实生活中,各种对象都是相互关联的,一种对象的改变会引起另一种对象的某种改变,开车遇到红绿灯,红灯停、绿灯行;气象局的天气预报数据改变,我们手机上的天气信息也得对应气息局的天气数据

在这里插入图片描述

气象局的天气数据变化,APP中的数据也要随着变化,这是观察者模式的思想


观察者模式

什么是观察者模式?

观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式

为什么要观察者模式?

对象间的存在依赖,我们想要如果观察目标改变,它会自动通知我们,而不是我们去查看观察目标是否改变

例如:气象局天气数据改变,我们希望气象局自动把数据送到我们的APP中,而不是我们去查看气象局的天气数据

发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展

建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应

观察者模式结构

在这里插入图片描述

模式角色:

  • 抽象主题(Subject)角色:也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法

  • 具体主题(Concrete Subject)角色:也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象

  • 抽象观察者(Observer)角色:它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用

  • 具体观察者(Concrete Observer)角色:实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态


模式实现案例

对于气象局的天气信息,有:temperature气温,pressure气压,humidity湿度

有两个网站注册到了气象局,气象局每次更新信息都会更新网站的天气信息

在这里插入图片描述

package com.company.Behavioral.observer;

import java.util.ArrayList;

//抽象观察者
interface Observer{
    public void update(float temperature,float pressure,float humidity);
}
//具体观察者:QQ
class QQ implements Observer{
    private float temperature;
    private float pressure;
    private float humidity;
    //更新信息
    @Override
    public void update(float temperature, float pressure, float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        display();
    }
    //展示天气信息
    private void display(){
        System.out.println("****Today: temperature ="+ temperature + "****");
        System.out.println("****Today: pressure ="+ pressure + "****");
        System.out.println("****Today: humidity ="+ humidity + "****");
    }
}
//具体观察者:baidu
class Baidu implements Observer{
    private float temperature;
    private float pressure;
    private float humidity;

    @Override
    public void update(float temperature, float pressure, float humidity) {
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        display();
    }

    private void display(){
        System.out.println("****百度网站气温: temperature ="+ temperature + "****");
        System.out.println("****百度网站气压: pressure ="+ pressure + "****");
        System.out.println("****百度网站湿度: humidity ="+ humidity + "****");
    }
}

//接口,由子类实现
interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObserver();
}

class WeatherData implements Subject{
    private float temperature;
    private float pressure;
    private float humidity;
    //观察者集合
    private ArrayList<Observer> observers;

    public WeatherData() {
        this.observers = new ArrayList<Observer>();
    }
    //注册一个观察者
    @Override
    public void registerObserver(Observer o) {
        observers.add(o);
    }
    //删除一个观察者
    @Override
    public void removeObserver(Observer o) {
        if (observers.contains(o)) {
            observers.remove(o);
        }
    }
    //遍历所有观察者,并通知
    @Override
    public void notifyObserver() {
        for (int i = 0 ; i < observers.size() ; i++){
            observers.get(i).update(this.temperature,this.pressure,this.humidity);
        }
    }
    //数据改变时,通知所有观察者
    public void dataChange(){
        notifyObserver();
    }
    //数据改变接口
    public void setData(float temperature, float pressure, float humidity){
        this.temperature = temperature;
        this.pressure = pressure;
        this.humidity = humidity;
        dataChange();
    }
}
class Client{
    public static void main(String[] args) {
        //创建一个WeatherData
        WeatherData weatherData = new WeatherData();
        //创建观察者
        QQ qq = new QQ();
        Baidu baidu = new Baidu();
        //注册
        weatherData.registerObserver(qq);
        weatherData.registerObserver(baidu);
        //数据改变
        System.out.println("通知各个观察者");
        weatherData.setData(10,100,30);
    }
}

在这里插入图片描述

气象局的每一次数据变化,都会自动遍历注册到WeatherData上的Observer,更新Observer的数据


模式优缺点

已经使用过观察者模式,在使用的过程中,可以分析思考一下观察者模式的优缺点

优点

  • 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色(所谓的MVC模式基于观察者模式的思想,数据改变Model表现层也随之改变)
  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合
  • 观察者模式支持广播通信
  • 观察者模式符合“开闭原则”的要求

缺点

  • 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化

适用场景

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度
  • 一个对象必须通知其他对象,而并不知道这些对象是谁
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制

真实的使用

  1. JavaWeb中的监听器Listener

可以看真的了解Listener吗?- Listener详解

JavaWeb中的各种监听器就是基于观察者模式,也可以称为监听者模式

例如:ServletContextListener,它是监听服务器中ServletContext域对象的创建与销毁,不过,有点不同的是,它将ServletContext的生命周期封装成事件对象,观察者接收事件对象,即可触发观察者的自定义的方法

其实我们前面的气象局案例中,也可以把天气数据封装成一个对象,对象改变就是一个事件

  1. 事件处理模式

JDK1.1版本及以后的各个版本中,事件处理模型采用基于观察者模式的委派事件模型(Delegation Event Model, DEM)

事件的发布者称为事件源(Event Source),而订阅者叫做事件监听器(Event Listener),在这个过程中还可以通过事件对象(Event Object)来传递与事件相关的信息
事件源对象、事件监听对象(事件处理对象)和事件对象构成了Java事件处理模型的三要素

  1. MVC模式

SpringMVC中的MVC,就是指MVC模式

MVC模式是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。观察者模式可以用来实现MVC模式,观察者模式中的观察目标就是MVC模式中的模型(Model),而观察者就是MVC中的视图(View),控制器(Controller)充当两者之间的中介者(Mediator)。当模型层的数据发生改变时,视图层将自动改变其显示内容

SpringMVC就是数据持久层、服务层、控制层、视图层的层次的分割,通过前端控制器 DispatcherServlet、处理执行链HandlerExcutionChian、处理器映射器 HandlerMapping、处理器适配器 HandlerAdapter、视图解析器 ViewResolver等组件实现层次间的数据传输,完成解耦合

总的来说,观察者模式是一种很有用的模式,也经常使用


总结

  • 观察者模式:定义对象间的依赖关系,当目标对象状态改变时,自动通知其相关依赖对象并自动更新
  • 观察者模式有四个角色:抽象目标类、具体目标类、抽象观察者、具体观察者
  • 具体目标类实现了抽象目标类的抽象方法,当自身状态改变时自动通知注册在其上的观察者;具体观察者实现了抽象观察者的方法,在具体目标类通知时自动更新自身状态
  • 观察者模式的优点:实现表示层和数据逻辑层的分离;支持广播通信;在观察目标和观察者中建立一个抽象的耦合
  • 观察者模式的缺点:当观察者过多,通知时需要很长时间;如果观察者和目标类有循环依赖会系统崩溃
  • 观察者模式适用情况:抽象模型存在依赖关系,一个对象的改变会导致其他对象的改变,而不知道具体有多少对象发生了改变;系统需要一个触发链
  • 观察者模式有很多实际使用,如JavaWeb中的组件Listener,Java中的事件处理模式,SpringMVC中的MVC模式即使用了观察者模式的思想
发布了121 篇原创文章 · 获赞 31 · 访问量 7869

猜你喜欢

转载自blog.csdn.net/key_768/article/details/105481022