当前位置: 首页 > news >正文

《图解设计模式》笔记(八)管理状态

十七、Observer模式:发送状态变化通知

Observer :“进行观察的人”,也就是“观察者”。

在 Observer模式中,当观察对象的状态发生变化时,会通知给观察者。

适用场景:根据对象状态进行相应处理.

示例程序:观察者将观察一个会生成数值的对象,并将它生成的数值结果显示出来。不同的观察者的显示方式不一样:DigitObserver以数字形式显示数值,GraphObserver以简单的图示形式显示数值。

示例程序类图

在这里插入图片描述

Observer

public interface Observer {
   
    public abstract void update(NumberGenerator generator);
}

NumberGenerator

import java.util.ArrayList;
import java.util.Iterator;

public abstract class NumberGenerator {
   
    private ArrayList observers = new ArrayList();        // 保存Observer们
    public void addObserver(Observer observer) {
       // 注册Observer
        observers.add(observer);
    }
    public void deleteObserver(Observer observer) {
    // 删除Observer
        observers.remove(observer);
    }
    public void notifyObservers() {
                  // 向Observer发送通知
        Iterator it = observers.iterator();
        while (it.hasNext()) {
   
            Observer o = (Observer)it.next();
            o.update(this);
        }
    }
    public abstract int getNumber();                // 获取数值
    public abstract void execute();                 // 生成数值
}

RandomNumberGenerator

import java.util.Random;

public class RandomNumberGenerator extends NumberGenerator {
   
    private Random random = new Random();   // 随机数生成器
    private int number;                     // 当前数值
    public int getNumber() {
                   // 获取当前数值
        return number;
    }
    public void execute() {
   
        for (int i = 0; i < 20; i++) {
   
            number = random.nextInt(50);
            notifyObservers();
        }
    }
}

DigitObserver

public class DigitObserver implements Observer {
   

    // 接收参数为NumberGenerator的实例,然后通过调用NumberGenerator类的实例的getNumber方法可以获取到当前的数值,并将这个数值显示出来。
    public void update(NumberGenerator generator) {
   
        System.out.println("DigitObserver:" + generator.getNumber());
        try {
   
            // 为能够看清它是如何显示数值的,使用Thread.sleep来降低程序的运行速度。
            Thread.sleep(100);
        } catch (InterruptedException e) {
   
        }
    }
}

GraphObserver

public class GraphObserver implements Observer {
   
    public void update(NumberGenerator generator) {
   
        System.out.print("GraphObserver:");
        int count = generator.getNumber();
        for (int i = 0; i < count; i++) {
   
            System.out.print("*");
        }
        System.out.println("");
        try {
   
            Thread.sleep(100);
        } catch (InterruptedException e) {
   
        }
    }
}

Main

public class Main {
   
    public static void main(String[] args) {
   
        NumberGenerator generator = new RandomNumberGenerator();
        Observer observer1 = new DigitObserver();
        Observer observer2 = new GraphObserver();
        generator.addObserver(observer1);
        generator.addObserver(observer2);
        generator.execute();
    }
}

角色

在这里插入图片描述

  • Subject(观察对象)
    定义了注册观察者和删除观察者的方法。
    声明了“获取现在的状态”的方法。
    示例中是NumberGenerator类。
  • ConcreteSubject(具体的观察对象)
    当自身状态发生变化后,它会通知所有已经注册的Observer角色。
    示例中是RandomNumberGenerator类。
  • Observer(观察者)
    接收来自Subject角色的状态变化的通知。为此,它声明了update方法。
    示例中是Observer接口。
  • ConcreteObserver (具体的观察者)
    当它的update方法被调用后,会去获取要观察的对象的最新状态。
    示例中是DigitObserver类和GraphObserver类。

拓展思路的要点

这里也出现了可替换性

使用设计模式的目的之一就是使类成为可复用的组件。
在本模式中,有带状态的ConcreteSubject角色和接收状态变化通知的ConcreteObserver角色。
连接这两个角色的就是它们的接口(API) Subject角色和Observer角色。

一方面RandomNumberGenerator类并不知道,也无需在意正在观察自己的(自己需要通知的对象)到底是DigitObserver类的实例还是GraphObserver类的实例。不过它知道在它的observers字段中所保存的观察者们都实现了Observer接口。因为这些实例都是通过addObserver方法注册的,这就确保了它们一定都实现了Observer接口,一定可以调用它们的update方法。

另一方面,DigitObserver类也无需在意自己正在观察的究竟是RandomNumberGenerator类的实例还是其他XXXXNumberGenerator类的实例。不过,DigitObserver类知道它们是NumberGenerator类的子类的实例,并持有getNumber方法。

利用抽象类和接口从具体类中抽出抽象方法

在将实例作为参数传递至类中,或者在类的字段中保存实例时,不使用具体类型,而是使用抽象类型和接口
这样的实现方式可以轻松替换具体类。

Observer的顺序

在示例的notifyObservers方法中,先注册的Observer的update方法会先被调用。
通常在设计时就要注意调用顺序。
在示例中,绝不能因为先调用 DigitObserver的update方法后调用 GraphObserver的update方法而导致应用程序不能正常工作。
通常,只要保持各个类的独立性,就不会有上面这种类的依赖关系混乱问题。
不过,还需要注意下面将要提到的情况。

当Observer的行为会对Subject产生影响时

在示例中,RandomNumberGenerator类会在自身内部生成数值,调用 update方法。
但在通常的 Observer模式中,也可能是其他类触发Subject角色调用update方法。
例如,在GUI应用程序中,多数情况下是用户按下按钮后会触发update方法被调用。
Observer角色也有可能会触发 Subject 角色调用 update方法。这时注意不要导致方法被循环调用。

Subject 状态发生变化

通知Observer

Observer调用 Subject 的方法

导致Subject状态发生变化

通知Observer

……

传递更新信息的方式

传递给u

相关文章:

  • 初窥强大,AI识别技术实现图像转文字(OCR技术)
  • 消防设施操作员考试题库及答案
  • 2024年12月电子学会青少年机器人技术等级考试(五级)理论综合真题
  • 广告深度学习计算:阿里妈妈大模型服务框架HighService
  • 快速部署 DeepSeek R1 模型
  • 深度学习之卷积神经网络框架模型搭建
  • DeepAR:一种用于时间序列预测的深度学习模型
  • STM32简介
  • 基于斜坡单元的机器学习模型预测滑坡易发性,考虑条件因素的异质性
  • 【python语言应用】最新全流程Python编程、机器学习与深度学习实践技术应用(帮助你快速了解和入门 Python)
  • 《open3d+pyqt》第一章——网格读取显示
  • 数值积分:通过复合梯形法计算
  • 浏览器打开Axure RP模型
  • 释放你的元数据:使用 Elasticsearch 的自查询检索器
  • 请求响应-请求-日期参数json参数路径参数
  • 使用PHP爬虫获取1688商品分类:实战案例指南
  • 2.14寒假作业
  • OpenCV识别电脑摄像头中的圆形物体
  • 利用docker-compose一键创建并启动所有容器
  • 【安全靶场】信息收集靶场
  • 重庆网站制作长沙/在线识别图片找原图
  • 一站式服务就像一个什么/百度经验app下载
  • 公寓注册公司需要什么条件/重庆百度推广排名优化
  • 建设一个电商网站的流程图/网上做广告宣传
  • 1号网站建设 高端网站建设/怎么做网站排名
  • 汕头建设网站的公司/专业网站建设公司首选