HarmonyOS Next 浅谈 发布-订阅模式
前言
其实在目前的鸿蒙应用开发中,或者大前端时代、vue、react、小程序等等框架、语言开发中,普通的使用者越来越少的会碰到必须要掌握设计模式的场景。大白话意思就是一些框架封装太好了,使用者只管在它们的体系下使用就行,哪怕不懂设计模式,也不妨碍我们正常开发业务。但是,如果碰到要封装一些工具、或者游戏开发的时候,那么设计模式的重要性就突显出来了。因为在做封装的时候,如果不使用一些设计模式,那么这些封装的代码基本无法使用。有同感的小伙伴可以踊跃发言。😄
目标
arkts 中,存在 Emitter 对象,它具有持续订阅事件和单次订阅事件、取消订阅事件、触发事件的能力。我们可以将它做为封装的参考,来自己实现一个类似的封装。
Emitter 的使用就是典型的发布-订阅的设计模式。也可以理解为(生产者-消费者设计模式)
- 订阅 理解为我们向邮局订阅一些报刊
- 发布 理解为报刊发布了,我们自然会受到对应的新报刊
- 对于订阅者来说
- 我们可以无限时长的订阅报刊(持续订阅)
- 我们可以只订阅一次报刊(单次订阅)
- 可以取消订阅的报刊
- 对于发布者来说
- 负责发布即可
接口设计
方法 | 说明 |
---|---|
on | 持续订阅 |
once | 单词订阅 |
off | 取消订阅 |
emit | 发布 |
具体实现
定义类型
- eventType 定义一个事件类型的联合类型,它可以是 normal 或者 once
- IEventItem 定义一个事件项的接口,包含事件 ID、类型、回调函数数组以及事件具体类型等属性
// 定义一个事件类型的联合类型,它可以是 "normal" 或者 "once"
type eventType = 'normal' | 'once'// 定义一个事件项的接口,包含事件ID、类型、回调函数数组以及事件具体类型等属性
interface IEventItem {eventId: numbertype: stringcbs: Function[]eventType: eventType
}
定义类的基本结构
- MyEmitter 为封装 Emitter 的自定义类的名称
- listeners 存储所有事件监听器的私有静态数组,初始为空
- _eventId 用于生成唯一事件 ID 的私有静态变量,初始值为 0
- **_on ** 私有静态方法,用于添加事件监听器 ,接受事件类型、事件名称和回调函数作为参数
- on 静态方法,用于添加普通类型的事件监听器
- once 静态方法,用于添加只触发一次的事件监听器
- emit 静态方法,用于触发指定类型的事件,会遍历该事件类型对应的所有回调函数并执行它们
- off 静态方法,用于移除指定事件 ID 的事件监听器,接受事件 ID 作为必选参数,可选地接受一个回调函数作为参数,如果只传入事件 ID,将移除该 ID 对应的整个事件项;如果同时传入回调函数,将只移除该事件项中对应的回调函数
class MyEmitter {// 存储所有事件监听器的私有静态数组,初始为空private static listeners: IEventItem[] = [];// 用于生成唯一事件ID的私有静态变量,初始值为0private static _eventId: number = 0;// 私有静态属性的getter方法,每次调用返回递增后的_eventId值// 用于获取下一个可用的事件IDprivate static get eventId() {}// 私有静态方法,用于添加事件监听器// 接受事件类型、事件名称和回调函数作为参数private static _on(eventType: eventType, type: string, cb: Function) {}// 静态方法,用于添加普通类型的事件监听器// 接受事件名称和回调函数作为参数// 内部调用私有静态方法 _on 并传入 "normal" 事件类型static on(type: string, cb: Function) {}// 静态方法,用于添加只触发一次的事件监听器// 接受事件名称和回调函数作为参数// 内部调用私有静态方法 _on 并传入 "once" 事件类型static once(type: string, cb: Function) {}// 静态方法,用于触发指定类型的事件// 接受事件名称作为必选参数,可选地接受一个数据参数// 会遍历该事件类型对应的所有回调函数并执行它们static emit<T = undefined>(type: string, data?: T) {}// 静态方法,用于移除指定事件ID的事件监听器// 接受事件ID作为必选参数,可选地接受一个回调函数作为参数// 如果只传入事件ID,将移除该ID对应的整个事件项;如果同时传入回调函数,将只移除该事件项中对应的回调函数static off(eventId: number, cb?: Function) {}
调用示例
@Entry
@Component
struct Index {tid: number = -1build() {Column({ space: 10 }) {Button("1 注册常规事件").onClick(() => {this.tid = MyEmitter.on("login", (res: object) => {console.log(JSON.stringify(res))})})Button("1 取消常规事件").onClick(() => {MyEmitter.off(this.tid)})Button("1 触发常规事件").onClick(() => {MyEmitter.emit("login", 100)})Button("2 注册一次性事件").onClick(() => {this.tid = MyEmitter.once("login2", (res: object) => {console.log(JSON.stringify(res))})})Button("2 取消一次性事件").onClick(() => {MyEmitter.off(this.tid)})Button("2 触发一次性事件").onClick(() => {MyEmitter.emit("login2", 100)})Button("3 注册具名事件").onClick(() => {this.tid = MyEmitter.on("login1", this.fn1)})Button("3 取消具名事件").onClick(() => {MyEmitter.off(this.tid, this.fn1)})Button("3 触发具名事件").onClick(() => {MyEmitter.emit("login1", 300)})}.height('100%').width('100%')}fn1(n: number) {console.log("具名事件", n)}
}
效果图
总结
发布 - 订阅模式是一种非常有用的软件设计模式,它可以实现系统的解耦、可扩展性和灵活性。在实际应用中,需要根据具体的需求和场
景选择合适的实现方式
作者
作者:万少
链接:https://www.nutpi.net/
來源:坚果派 著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。