自定义事件观察者及扩展

事件观察者的应用

事件观察者又可以叫事件委托、订阅模式,目的是为了解偶,定义了一种一对多的关系,当事件变化时通知与此事件依赖的对象,并做出相应的处理。应用时非常广的,我在做游戏中时必定用到的,是最最基础的模块,数据更新、玩家动作触发、帧频刷新、服务器消息响应、界面与逻辑分离、状态变迁等等。我在理解观察者模式的基础上作出了一些改动,使用起来会更方便与快捷。

事件观察者

645887-20170102154528597-970303108.png

首先,事件观察者监听事件,然后当收到事件触发时,调用事件响应函数,完成一次事件的变迁。
那么,事件观察者内部会存在一个事件列表来维护事件绑定,即为 eventMap ,其中key是唯一值是事件ID,通过区分事件ID来划分事件监听函数。

  1. 一个事件ID对应多个响应事件
  2. 事件ID不可重复
  3. 同一个事件ID的事件响应函数不可重复。

645887-20170102154551800-865506412.png

事件列表中的每一个元素可以包涵4个元素,我使用TypeScript实现整个类。

eventID 事件ID
callback 事件响应函数
thisObj 作用域指针
once 是否触发一次

定义 EventDispatcher 类

module app {
    export class EventDispatcher {
        private _eventMap: any = {};

        public static create(): app.EventDispatcher {
            var instance = new app.EventDispatcher();
            return instance;
        }
    }
}

定义 on、once 监听函数

on 函数只要不移除事件,只要事件触发就会响应。
once 函数只监听一次事件,事件触发一次后就会移除。

public on(eventID: any, callback: Function, thisObj?: any): EventDispatcher {
    this.addEventListener(eventID, callback, thisObj);
    return this;
}

public once(eventID: any, callback: Function, thisObj?: any): EventDispatcher {
    this.addEventListener(eventID, callback, thisObj, true);
    return this;
}

public has(eventID: any, callback: Function, thisObj?: any): boolean {
    return this.hasEventListener(eventID, callback, thisObj);
}

private addEventListener(eventID: any, callback: Function, thisObj: any, once: boolean = false): void {
    if (this.hasEventListener(eventID, callback, thisObj)) return console.log('repeat add Event');
    var list: Array<any> = this._eventMap[eventID];
    if (!list) {
        list = [];
        this._eventMap[eventID] = list;
    }
    list.push({ eventID: eventID, callback: callback, thisObj: thisObj, once: once });
}

private hasEventListener(eventID: any, callback: Function, thisObj: any): boolean {
    var list: Array<any> = this._eventMap[eventID];
    if (!list) return false;
    var len: number = list.length;
    for (var idx = 0; idx < len; idx++) {
        var eventData = list[idx];
        if (eventData &&
            eventData.callback === callback &&
            eventData.thisObj === thisObj) {
            return true;
        }
    }
    return false;
}

定义 off、offAll、offTarget 移除函数

off 函数接受 事件ID、响应函数、作用域,根据这三个参数来确定移除哪个响应
offAll 函数接受 事件ID 时移除指定事件ID的所有响应函数列表,如果不传值,则删除所有的响应函数。
offTarget 函数接受对象指针,通过对象删除所有监听

public off(eventID: any, callback: Function, thisObj?: any): EventDispatcher {
    this.removeEventListener(eventID, callback, thisObj);
    return this;
}

public offAll(eventID?: any): EventDispatcher {
    if (eventID) {
        delete this._eventMap[eventID];
    }
    else {
        this._eventMap = {};
    }
    return this;
}

public offTarget(target: any): EventDispatcher {
    this.removeEventListenerByTarget(target);
    return this;
}

private removeEventListener(eventID: any, callback: Function, thisObj: any): void {
    var list: Array<any> = this._eventMap[eventID];
    var len: number = list.length;
    for (var idx = 0; idx < len; idx++) {
        var eventData = list[idx];
        if (eventData &&
            eventData.callback === callback &&
            eventData.thisObj === thisObj) {
            list.splice(idx, 1);
            break;
        }
    }
}

private removeEventListenerByTarget(thisObj: any): void {
    for (var key in this._eventMap) {
        var list: Array<any> = this._eventMap[key];
        this._eventMap[key] = list.filter(function (value): boolean {
            return !(value.thisObj === thisObj);
        }, this);
    }
}

定义 emit 函数

emit 函数接受事件ID、传递参数数据。
通过循环遍历依次响应事件列表,如果是once则响应后直接删除。

emit(事件, data);

public emit(eventID: any, data?: any): EventDispatcher {
    this.dispatchEvent(eventID, data);
    return this;
}

private dispatchEvent(eventID: any, data?: any): void {
    var list: Array<any> = this._eventMap[eventID];
    if (!list) return;
    var cloneList: Array<any> = list.slice(0);
    var len: number = cloneList.length;
    for (var idx = 0; idx < len; idx++) {
        var eventData = cloneList[idx];
        eventData.once && this.removeEventListener(eventID, eventData.callback, eventData.thisObj);
        eventData.callback.call(eventData.thisObj, data);
    }
}

扩展定义 all 函数

all 函数接受一个事件ID列表,目的是当函数列表内的所有函数都触发后,才触发响应函数。
这里使用了闭包,通过包内作用域,调用 once 函数监听及收集数据并响应函数。

all([ 事件1,事件2,事件3 ], callback, thisObj);

public all(events: any[], callback: Function, thisObj?: any): EventDispatcher {
    if (!events || events.length == 0 || !callback) throw 'events or callback is null!'
    let proxy = this;
    let datas: any = {};
    let eventsClone = events.concat();
    eventsClone.forEach(function (item) {
        proxy.once(item, function (data) {
            datas[item] = data;
            eventsClone.shift();
            if (eventsClone.length == 0) {
                setTimeout(function () {
                    callback.call(thisObj, datas);
                }, 0);
            }
        }, null);
    }, this);
    return this;
}

插件 plugin 模式

以上定义的函数满足事件观察者的所有特性,也作出了一些写法上的处理如链式调用,使代码更加优雅。
但是为了功能及便捷,没有永远适合的处理,因此我又加上了 plugin 模式,意思就是 EventDispatcher 可以作为另一个 EventDispatcher 子节点,当父节点的事件触发时,可以向下继续处理子节点的事件响应,直到处理完毕。

645887-20170102154614784-1612161307.png

  • 定义pluginMap收集所有的子节点
  • 定义EventDispatcher的唯一索引为Key
  • 避免子节点与父节点重复,造成死循环。
module app {
    export class EventDispatcher {
        private static EventDispatcher_Hashid: number = 1;
        public hashid: number = EventDispatcher.EventDispatcher_Hashid++;
        private _pluginMap: any = {};
        private _eventMap: any = {};

        public static create(): app.EventDispatcher {
            var instance = new app.EventDispatcher();
            return instance;
        }

        public plugin(ed: EventDispatcher): EventDispatcher {
            if (!ed) throw 'plugin is null'
            if (!this._pluginMap[ed.hashid])
                this._pluginMap[ed.hashid] = ed;
            return this;
        }

        public unplugin(ed: EventDispatcher): EventDispatcher {
            if (this._pluginMap[ed.hashid]) {
                delete this._pluginMap[ed.hashid];
            }
            return this;
        }

        public unplugins(): EventDispatcher {
            this._pluginMap = {};
            return this;
        }

        //修改后的 dispatchEvent 函数
        private dispatchEvent(eventID: any, data?: any): void {
            var list: Array<any> = this._eventMap[eventID];
            if (!list) return;
            var cloneList: Array<any> = list.slice(0);
            var len: number = cloneList.length;
            for (var idx = 0; idx < len; idx++) {
                var eventData = cloneList[idx];
                eventData.once && this.removeEventListener(eventID, eventData.callback, eventData.thisObj);
                eventData.callback.call(eventData.thisObj, data);
            }
            //增加插件的继续处理
            var list = [];
            for (var key in this._pluginMap) {
                list.push(this._pluginMap[key]);
            }
            list.forEach(function (ed) {
                ed.emit(eventID, data);
            }, this);
        }
    }
}

只要子节点监听了与父节点相同的事件,父节点触发事件,子节点也会响应。

var ed1 = EventDispatcher.create();
ed1.on('event1',callback1,this);
var ed2 = EventDispatcher.create();
ed2.on('event1',callback2,this);
ed1.plugin(ed2);
ed1.emit('event1');//ed2 同样会触发 event1 事件
ed2.emit('event1');//ed1 不会触发 event1 事件

到此为止,事件观察者的功能已经挺完善的了,但是需求时在变更的,如果有更好更便捷更有趣的功能,会继续加上!?

645887-20170102154633831-1313170960.png

转载于:https://www.cnblogs.com/Richard-Core/p/eventdispatcher-plugin.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值