基于观察者模式实现一个Event类

🤔 废话不多说,在实现Event类之前,让我们先了解一下什么是观察者模式

观察者模式

一, 定义

观察者模式(Observer Pattern)是一种常用的软件设计模式,用于构建对象之间的一对多依赖关系。在这种模式中,当一个对象(被观察者)的状态发生变化时,它会自动通知其他依赖于它的对象(观察者),使它们能够及时做出响应。观察者模式也叫 发布-订阅(Publish/Subscribe)模式,观察者模式是一种对象行为模式,是不是有点云里雾里,下面会举个生活中的小例子做解释

二,观察者模式涉及以下几个角色

  1. 被观察者(Subject):也称为主题或发布者,在该模式中,它维护了一组观察者对象,并提供注册、注销以及通知观察者的方法。当被观察者状态发生变化时,它会遍历观察者列表,调用每个观察者的更新方法。

  2. 观察者(Observer):也称为订阅者或监听者,观察者定义了一个更新方法,在接收到被观察者的通知时会调用该方法来执行相应的操作。观察者可以注册到一个或多个被观察者中。

二,模式特点

  1. 解偶性:被观察者和观察者之间通过接口进行通信,彼此之间解偶,可以独立修改和扩展。

  2. 易于扩展:可以在不修改被观察者的情况下增加新的观察者。

  3. 支持广播通信:被观察者可以同时通知多个观察者,实现一对多的依赖关系。

  4. 观察者模式符合'关闭原则'的要求

三,模式缺点

  1. 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。

  2. 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

四,生活中的观察者模式

耐克这几年出了很多爆款,小明看中了其中某一款鞋,于是去耐克店购买,但是到了之后,售货员告诉他今天断货了,可是他又不想每天都跑过来问,于是就把自己的电话留给了店员小姐姐,等到有货的时候就让小姐姐打电话通知他,等到他拿到货之后这件事也算完成了,那么小姐姐就不需要在给他打电话了

在这个例子中,小明是观察者,而售货员小姐姐是被观察者。

小明希望得到关于鞋子货物状态的通知,因此他把自己的电话留给了售货员小姐姐。这使得小姐姐成为被观察者,因为她需要在货物到货时通知小明。

售货员小姐姐在这种情况下充当被观察者的角色,因为她需要在鞋子有货时通知观察者(小明)。她会监测货物的到货情况,并负责与小明进行沟通,以确保他及时获取到所需的货物。

观察者模式的使用

js 复制代码
let el = doucument.querySelector('#box')
el.addEventListener('mousedown', (e) => {
    console.log(e)
})

我们常见的给dom元素绑定事件,就是发布订阅模式,我们想要知道鼠标按下的行为,但不知道用户什么时候按下,所以我们订阅这个mousedown事件,当鼠标被按下时就会向订阅者发布消息,这时我们就可以做对应的操作

创建一个观察者对象

首先我们创建一个观察者对象,它包含一个消息容器和三个方法,分别为订阅消息方法on,移除订阅消息方法off,发送订阅消息dispatch 通过定义一个event类来实现

js 复制代码
class event {
    events = {} // 事件池也就是消息容器,用来记录所有相关事件及处理函数
    on() {} // 注册消息接口
    off() {} // 取消订阅接口
    dispatch() {} // 触发接口
}

这样一个观察者对象雏形就出来了

创建一个事件池

js 复制代码
events = {}
/* eg:
    events = {
        "click":[f1,f2,f3......],
        "mousemove": [f1,f2,f3......]
    }
*/

注册消息

注册消息的目的是将订阅者注册的消息推入到消息队列中,需要两个参数:消息类型和对应的处理函数,在推入消息之前需要先判断队列中是否存在此消息

js 复制代码
on(eventName, fn) { // 添加一个事件处理 eventName事件名,fn 方法
    if(!this.events[eventName]) {
        // 如果此消息不存在创建一个该消息类型
        this.events[eventName] = [];
    };
    if(!this.events[eventName].includes(fn)) {
        // 将执行方法推入到该消息对应的执行队列中
        this.events[eventName].push(fn);
    };
}

发布订阅消息

其功能就是将所有订阅者订阅的消息依次执行,同样需要两个参数,分别是消息类型,和对应执行函数所需的参数

js 复制代码
dispatch(eventName, ...arg) {
    if(!this.events[eventName]) { // 如果没有添加过改消息类型则直接跳出
        return ;
    }
    this.events[eventName].forEach(item => {
        item.call(this, ...arg);
    })
}

移除订阅消息

移除订阅消息方法的作用是将订阅者注销的消息从队列中清除掉,因为需要知道要移除的消息类型和执行方法,所以也需要两个参数

js 复制代码
off(eventName, fn) {
    // 需要校验消息是否存在
    if(!this.events[eventName]) {
        return ; // 直接跳出
    }
    this.events[eventName] = this.events[eventName].filter(item => item !== fn)
}

好了,到此,我们基于观察者模式实现了一个基本的Event类, 全部代码如下

js 复制代码
class Event {
    // 创建一个事件池记录所有的相关事件及处理函数
    events = {}
    // 添加一个事件处理 eventName事件名,fn 方法
    on(eventName, fn){
        // 如果不存在这个事件 直接跳出
        if(!this.events[eventName]){
            this.events[eventName] = [];
        }
        // 验重
        if(!this.events[eventName].includes(fn)) {
            this.events[eventName].push(fn);
        }
    }
    // 添加一个删除事件
    off(eventName, fn){
        if(!this.events[eventName]){
            return;
        }
        this.events[eventName] = this.events[eventName].filter(item => item !== fn)
    }
    // 负责把触发到的事件给执行了
    dispatch(eventName, ...arg) {
        // 如果不存在这个事件 直接跳出
        if(!this.events[eventName]){
            return;
        }
        this.events[eventName].forEach(item => {
            item.call(this, ...arg)
        })
    }
}

做一些简单的测试看看效果如何

js 复制代码
let event = new Event();
event.on('drag', () => {
    console.log('你想怎样拖拽')
})
event.on('drag', () => {
    console.log('随便拖拽')
})
event.dispatch('drag')

控制台可以看到打印结果如下

取消消息订阅会如何呢

js 复制代码
let event = new Event();
let f = () => {
    console.log('我会被移除')
}
event.on('drag', f)
event.off('drag', f)
event.dispatch('drag')

执行结果如下 我们发现添加在drag消息类型中的执行方法全部被清理掉了

到此我们成功的基于观察者模式实现了一个Event类,后期我们会基于这个类实现一些特殊的拖拽场景,

敬请期待!!!!!

相关推荐
小白不太白9504 小时前
设计模式之建造者模式
java·设计模式·建造者模式
菜菜-plus6 小时前
java 设计模式 模板方法模式
java·设计模式·模板方法模式
萨达大6 小时前
23种设计模式-模板方法(Template Method)设计模式
java·c++·设计模式·软考·模板方法模式·软件设计师·行为型设计模式
机器视觉知识推荐、就业指导8 小时前
C++设计模式:原型模式(Prototype)
c++·设计模式·原型模式
阳光开朗_大男孩儿8 小时前
组合模式和适配器模式的区别
设计模式·组合模式·适配器模式
MinBadGuy8 小时前
【GeekBand】C++设计模式笔记13_Flyweight_享元模式
c++·设计模式
Clang's Blog9 小时前
23种设计模式详解(以Java为例)
java·开发语言·设计模式
程序员奇奥10 小时前
设计模式——简单工厂模型、工厂模式、抽象工厂模式、单例模式、代理模式、模板模式
单例模式·设计模式·抽象工厂模式
hxj..10 小时前
【设计模式】代理模式
java·设计模式·代理模式·动态代理
十五年专注C++开发11 小时前
C++不完整类型(Incomplete Type)的检测与避免
开发语言·c++·算法·设计模式