第十五章 观察者模式

news2024/12/23 22:37:08

目录

1 观察者模式介绍

2 观察者模式原理

3 观察者模式实现

4 观察者模式应用实例

 5 观察者模式总结


1 观察者模式介绍

观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者 一些产品的设计思路,都有这种模式的影子.

现在我们常说的基于事件驱动的架构,其实也是观察者模式的一种最佳实践。当我们观察某一个对象时,对象传递出的每一个行为都被看成是一个事件,观察者通过处理每一个事件来完成自身的操作处理。

观察者模式(observer pattern)的原始定义是:定义对象之间的一对多依赖关系,这样当一个对象改变状态时,它的所有依赖项都会自动得到通知和更新。

2 观察者模式原理

  • Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
  • ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
  • Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
  • ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要与具体目标保持一致.

3 观察者模式实现

  • 观察者
/**
 * 抽象观察者
 * @author spikeCong
 * @date 2022/10/11
 **/
public interface Observer {

    //update方法: 为不同观察者的更新(响应)行为定义相同的接口,不同的观察者对该方法有不同的实现
    public void update();
}

/**
 * 具体观察者
 * @author spikeCong
 * @date 2022/10/11
 **/
public class ConcreteObserverOne implements Observer {

    @Override
    public void update() {
        //获取消息通知,执行业务代码
        System.out.println("ConcreteObserverOne 得到通知!");
    }
}

/**
 * 具体观察者
 * @author spikeCong
 * @date 2022/10/11
 **/
public class ConcreteObserverTwo implements Observer {

    @Override
    public void update() {
        //获取消息通知,执行业务代码
        System.out.println("ConcreteObserverTwo 得到通知!");
    }
}
  • 被观察者
/**
 * 抽象目标类
 * @author spikeCong
 * @date 2022/10/11
 **/
public interface Subject {

     void attach(Observer observer);
     void detach(Observer observer);
     void notifyObservers();
}

/**
 * 具体目标类
 * @author spikeCong
 * @date 2022/10/11
 **/
public class ConcreteSubject implements Subject {

    //定义集合,存储所有观察者对象
    private ArrayList<Observer> observers = new ArrayList<>();


    //注册方法,向观察者集合中增加一个观察者
    @Override
    public void attach(Observer observer) {
        observers.add(observer);
    }

    //注销方法,用于从观察者集合中删除一个观察者
    @Override
    public void detach(Observer observer) {
        observers.remove(observer);
    }

    //通知方法
    @Override
    public void notifyObservers() {
        //遍历观察者集合,调用每一个观察者的响应方法
        for (Observer obs : observers) {
            obs.update();
        }
    }
}
  • 测试类
public class Client {

    public static void main(String[] args) {
        //创建目标类(被观察者)
        ConcreteSubject subject = new ConcreteSubject();

        //注册观察者类,可以注册多个
        subject.attach(new ConcreteObserverOne());
        subject.attach(new ConcreteObserverTwo());

        //具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
        subject.notifyObservers();
    }
}

4 观察者模式应用实例

1 ) 未使用设计模式

/**
 * 模拟买房摇号服务
 * @author spikeCong
 * @date 2022/10/11
 **/
public class DrawHouseService {

    //摇号抽签
    public String lots(String uId){
        if(uId.hashCode() % 2 == 0){
            return "恭喜ID为: " + uId + " 的用户,在本次摇号中中签! !";
        }else{
            return "很遗憾,ID为: " + uId + "的用户,您本次未中签! !";
        }
    }
}

public class LotteryResult {

    private String uId; // 用户id
    private String msg; // 摇号信息
    private Date dataTime; // 业务时间
    
    
	//get&set.....
}

/**
 * 开奖服务接口
 * @author spikeCong
 * @date 2022/10/11
 **/
public interface LotteryService {

    //摇号相关业务
    public LotteryResult lottery(String uId);
}


/**
 * 开奖服务
 * @author spikeCong
 * @date 2022/10/11
 **/
public class LotteryServiceImpl implements LotteryService {

    //注入摇号服务
    private DrawHouseService houseService = new DrawHouseService();

    @Override
    public LotteryResult lottery(String uId) {
        //摇号
        String result = houseService.lots(uId);

        //发短信
        System.out.println("发送短信通知用户ID为: " + uId + ",您的摇号结果如下: " + result);

        //发送MQ消息
        System.out.println("记录用户摇号结果(MQ), 用户ID:" +  uId + ",摇号结果:" + result);

       	return new LotteryResult(uId,result,new Date());
    }
}

@Test
public void test1(){
    LotteryService ls = new LotteryServiceImpl();
    String result  = ls.lottery("1234567887654322");
    System.out.println(result);
}

1 ) 使用观察者模式进行优化

  • 事件监听
/**
 * 事件监听接口
 * @author spikeCong
 * @date 2022/10/11
 **/
public interface EventListener {

    void doEvent(LotteryResult result);
}

/**
 * 短信发送事件
 * @author spikeCong
 * @date 2022/10/11
 **/
public class MessageEventListener implements EventListener {

    @Override
    public void doEvent(LotteryResult result) {
        System.out.println("发送短信通知用户ID为: " + result.getuId() +
                ",您的摇号结果如下: " + result.getMsg());
    }
}

/**
 * MQ消息发送事件
 * @author spikeCong
 * @date 2022/10/11
 **/
public class MQEventListener implements EventListener {

    @Override
    public void doEvent(LotteryResult result) {
        System.out.println("记录用户摇号结果(MQ), 用户ID:" +  result.getuId() +
                ",摇号结果:" + result.getMsg());
    }
}

 事件处理

/**
 * 事件处理类
 * @author spikeCong
 * @date 2022/10/11
 **/
public class EventManager {

    public enum EventType{
        MQ,Message
    }

    //监听器集合
    Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>();

    public EventManager(Enum<EventType>... operations) {
        for (Enum<EventType> operation : operations) {
            this.listeners.put(operation,new ArrayList<>());
        }
    }

    /**
     * 订阅
     * @param eventType 事件类型
     * @param listener  监听
     */
    public void subscribe(Enum<EventType> eventType, EventListener listener){
        List<EventListener> users = listeners.get(eventType);
        users.add(listener);
    }

    /**
     * 取消订阅
     * @param eventType 事件类型
     * @param listener  监听
     */
    public void unsubscribe(Enum<EventType> eventType,EventListener listener){
        List<EventListener> users = listeners.get(eventType);
        users.remove(listener);
    }

    /**
     * 通知
     * @param eventType 事件类型
     * @param result    结果
     */
    public void notify(Enum<EventType> eventType, LotteryResult result){
        List<EventListener> users = listeners.get(eventType);
        for (EventListener listener : users) {
            listener.doEvent(result);
        }
    }

摇号业务处理

/**
 * 开奖服务接口
 * @author spikeCong
 * @date 2022/10/11
 **/
public abstract class LotteryService{

    private EventManager eventManager;

    public LotteryService(){
        //设置事件类型
        eventManager = new EventManager(EventManager.EventType.MQ, EventManager.EventType.Message);
        //订阅
        eventManager.subscribe(EventManager.EventType.Message,new MessageEventListener());
        eventManager.subscribe(EventManager.EventType.MQ,new MQEventListener());
    }

    public LotteryResult lotteryAndMsg(String uId){
        LotteryResult result = lottery(uId);
        //发送通知
        eventManager.notify(EventManager.EventType.Message,result);
        eventManager.notify(EventManager.EventType.MQ,result);

        return result;
    }

    public abstract LotteryResult lottery(String uId);
}

/**
 * 开奖服务
 * @author spikeCong
 * @date 2022/10/11
 **/
public class LotteryServiceImpl extends LotteryService {

    //注入摇号服务
    private DrawHouseService houseService = new DrawHouseService();

    @Override
    public LotteryResult lottery(String uId) {
        //摇号
        String result = houseService.lots(uId);

        return new LotteryResult(uId,result,new Date());
    }
}

测试

@Test
public void test2(){
    LotteryService ls = new LotteryServiceImpl();
    LotteryResult result  = ls.lotteryAndMsg("1234567887654322");
    System.out.println(result);
}

 5 观察者模式总结

1) 观察者模式的优点

  • 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
  • 被观察者发送通知,所有注册的观察者都会收到信息【可以实现广播机制】

2) 观察者模式的缺点

  • 如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时
  • 如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃

3 ) 观察者模式常见的使用场景

  • 当一个对象状态的改变需要改变其他对象时。比如,商品库存数量发生变化时,需要通知商品详情页、购物车等系统改变数量。
  • 一个对象发生改变时只想要发送通知,而不需要知道接收者是谁。比如,订阅微信公众号的文章,发送者通过公众号发送,订阅者并不知道哪些用户订阅了公众号。
  • 需要创建一种链式触发机制时。比如,在系统中创建一个触发链,A 对象的行为将影响 B 对象,B 对象的行为将影响 C 对象……这样通过观察者模式能够很好地实现。
  • 微博或微信朋友圈发送的场景。这是观察者模式的典型应用场景,一个人发微博或朋友圈,只要是关联的朋友都会收到通知;一旦取消关注,此人以后将不会收到相关通知。
  • 需要建立基于事件触发的场景。比如,基于 Java UI 的编程,所有键盘和鼠标事件都由它的侦听器对象和指定函数处理。当用户单击鼠标时,订阅鼠标单击事件的函数将被调用,并将所有上下文数据作为方法参数传递给它。

4 ) JDK 中对观察者模式的支持

JDK中提供了Observable类以及Observer接口,它们构成了JDK对观察者模式的支持.

  • java.util.Observer 接口: 该接口中声明了一个方法,它充当抽象观察者,其中声明了一个update方法.

    void update(Observable o, Object arg);
    
  • java.util.Observable 类: 充当观察目标类(被观察类) , 在该类中定义了一个Vector集合来存储观察者对象.下面是它最重要的 3 个方法。

    • void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。
    • void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。

    • void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1840643.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

“AI引擎”澎湃动力!深圳机场携手实在智能打造民航财务数字员工

近日&#xff0c;深圳机场集团&#xff08;000089&#xff09;与实在智能达成合作&#xff0c;引入了业内领先的平台级自动化产品——实在RPA数字员工&#xff0c;部署实在智能集“自动化平台开箱即用解决方案咨询”为一体的数字员工解决方案&#xff0c;实现纳税申报、报表制作…

一键简易桌签(带背景)-Word插件-大珩助手

问题整理&#xff1a; 如何Word中设计简易桌签&#xff1f;如何设置带背景图的桌签&#xff1f; Word大珩助手是一款功能丰富的Office Word插件&#xff0c;旨在提高用户在处理文档时的效率。它具有多种实用的功能&#xff0c;能够帮助用户轻松修改、优化和管理Word文件&…

3ds Max软件下载安装:3D建模软件 轻松开启你的建模之旅!

3ds Max&#xff0c;在建模过程中&#xff0c;网格建模和NURBS建模两大技术发挥着不可或缺的作用。网格建模允许用户通过顶点、边和面等元素的调整&#xff0c;精确地塑造出模型的形态&#xff1b;而NURBS建模则以其优秀的曲线和曲面处理能力&#xff0c;为设计师们提供了更为平…

el-table动态表头 浏览器大小改变产生表格错位问题

问题描述 使用element-ui的table组件时,表头由前端发送请求后动态更新,左侧编号列设定为fixed:left左侧固定定位;在改变屏幕大小(主副屏互转)时出现表格错位问题. 问题分析 element-ui的官方文档对该情况提供了一个doLayout方法来解决该问题; doLayout:对 Table 进行重新布局。…

ICMAN触摸芯片——大金属触摸

ICMAN触摸芯片之大金属触摸 按照工业级别设计 稳定性与抗干扰能力强 单颗芯片支持 1~12按键 可以轻松实现 金属灯触摸方案的触摸开关&#xff0b;无极调光 高性能ICMAN触摸芯片在金属LED触摸台灯的应用

PDF为何成为职场必备?编辑不求人,这几款工具助你一臂之力

不管是工作和学习&#xff0c;我们使用PDF文档的频率非常高&#xff0c;但是说起为什么要用PDF&#xff0c;却又只知其一不知其二。 为什么我们需要PDF&#xff1f; PDF&#xff0c;全称为Portable Document Format&#xff08;可移植文件格式&#xff09;&#xff0c;是一种…

网络校时服务器:铁路对时有妙招

在信息高速发展的今天&#xff0c;铁路作为国家的经济大动脉&#xff0c;与广大市民生活息息相关&#xff0c;担负着运送大流量乘客、保证交通畅通的重任&#xff0c;为了保证列车的正点运行和乘客的行程&#xff0c;对时间精准度的要求是非常严格的。随着我国铁路的发展速度和…

LSM-Tree数据结构原理

LSM-Tree树原理 什么是LSM-Tree LSM-Tree 即 Log Structrued Merge Tree&#xff0c;这是一种分层有序&#xff0c;硬盘友好的数据结构。核心思想是利用磁盘顺序写性能远高于随机写。 LSM-Tree 并不是一种严格的树结构&#xff0c;而是一种内存磁盘的多层存储结构。HBase、L…

newtonsoft.json动态读取json以及动态生成

问题 同一个接口返回不同类型的json&#xff0c;json结构相差比较大转换为C#对象不太合适&#xff0c;想着是否可以动态解析。 newtonsoft类 JTokenType类型 namespace Newtonsoft.Json.Linq {/// <summary>/// Specifies the type of token./// </summary>publ…

快速学习 JavaFX 进行 GUI 应用开发

JavaFX是Java的一个用于构建丰富图形用户界面的框架。通过JavaFX&#xff0c;开发者可以创建功能强大、交互性强且美观的桌面应用程序。 一、JavaFX简介 1. JavaFX的历史和现状 JavaFX最初作为Java的一部分发布&#xff0c;旨在替代Swing作为Java的主要GUI工具包。JavaFX提供…

uni-app中使用富文本rich-text个人经验

rich-text是在uni-app一个内置组件&#xff0c;用于高性能地渲染富文本内容。先贴一下官方的属性列表&#xff1a; 先说一下“selectable” 长按选择区域复制&#xff0c;这个我在APP项目中 不起作用&#xff0c;可能像文档说的&#xff0c;只支持“百度小程序”吧。在APP端起作…

为数据安全护航,袋鼠云在数据分类分级上的探索实践

在大数据时代&#xff0c;数据具有多源异构的特性&#xff0c;且价值各异&#xff0c;企业需依据数据的重要性、价值指数等予以区分&#xff0c;以利采取不同的数据保护举措&#xff0c;避免数据泄露。故而&#xff0c;数据分类分级管理属于数据安全保护中极为重要的环节之一。…

正则表达式写起来不简单,但用起来真香

说在前面 &#x1f388;整理一些常见常用的正则表达式。 常见的正则表达式 1、手机号码 /^[1][3456789][0-9]{9}$/这个正则表达式 /^[1][3456789][0-9]{9}$/ 用于匹配中国的手机号码的一部分&#xff0c;但不包括全部有效的手机号码格式。下面是对它的详细解释&#xff1a; ^…

项目三OpenStack基础环境配置与API使用

任务一 了解OpenStack基础环境配置 1.1 •数据库服务器 1.2 •消息队列服务 •AMQP系统的组成 任务二 了解并使用OpenStack API 2.1 •什么是RESTful API • RESTful API 是目前比较成熟的 一套Internet应用程序的API软件架构 。 • 表现 层&#xff08; Representation …

JavaWeb项目配置教程

将你的项目&#xff08;只有代码的文件&#xff0c;不是整个文件&#xff09;拖入idea 找到数据库配置代码&#xff08;一般在Util包里面&#xff0c;或者是properties配置文件&#xff09;并将密码修改为你的数据库密码。 点击Edit Configurations 点击Configure&#xff0…

突破数据存储瓶颈!转转业财系统亿级数据存储优化实践--图文解析

突破数据存储瓶颈&#xff01;转转业财系统亿级数据存储优化实践–图文解析 原文链接&#xff1a;https://juejin.cn/post/7358704806779437097 原文作者&#xff1a;转转技术团队 业财系统&#xff1a;业务和财务一体化系统 与传统财务记账不同&#xff0c;一笔金额不再是…

LED电子看板在提升企业数字化管理的应用价值

本文详细探讨 LED 电子看板在企业数字化管理中的应用价值&#xff0c;包括实时数据展示、可视化管理、提高生产效率、优化决策等方面&#xff0c;通过实际案例分析&#xff0c;阐述了其对企业发展的重要性。在当今数字化时代&#xff0c;企业管理面临着越来越多的挑战和机遇。为…

企业IT资源使用共享云桌面集中管理的优势

随着信息技术的飞速发展&#xff0c;企业面临着日益增长的IT资源需求和管理挑战。为了解决这一问题&#xff0c;越来越多的企业开始采用云桌面集中管理方案&#xff0c;以实现IT资源的共享和优化。 为保持企业各部门信息化和数字化业务顺利运转&#xff0c;IT需要耗费大量支持…

【BFS算法】广度搜索·由起点开始逐层向周围扩散求得最短路径(算法框架+题目)

0、前言 深度优先搜索是DFS&#xff08;Depth Frst Search)&#xff0c;其实就是前面所讲过的回溯算法&#xff0c;它的特点和它的名字一样&#xff0c;首先在一条路径上不断往下&#xff08;深度&#xff09;遍历&#xff0c;获得答案之后再返回&#xff0c;再继续往下遍历。…

使用SQLite

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 与许多其他数据库管理系统不同&#xff0c;SQLite不是一个客户端/服务器结构的数据库引擎&#xff0c;而是一种嵌入式数据库&#xff0c;它的数据库就…