全国免费咨询:

13245491521

VR图标白色 VR图标黑色
X

中高端软件定制开发服务商

与我们取得联系

13245491521     13245491521

2024-01-24_优雅的Kotlin事件处理 - 重构

您的位置:首页 >> 新闻 >> 行业资讯

优雅的Kotlin事件处理 - 重构 通过这些专业的重构技巧, 释放 Kotlin 代码的峰值性能. 在软件开发的世界里, 代码重构是将我们从纠结而低效的代码中解救出来的英雄. 在本文中, 我们将开始一场冒险, 用以重构处理各种事件的 Kotlin 代码. 我们的任务是什么? 提高性能和风格, 使代码更流畅, 更易维护, 并让我们的工作充满乐趣. 我们的目标在改造 Kotlin 事件处理的过程中, 我们的目标是完善代码, 使其更高效, 更易读, 更易维护. 我们将引入各种改进, 包括: 用HashMap代替复杂的when语句, 以获得快如闪电的(O(1))性能.使用内联函数和reified类型参数来优化语法.采用委托属性, 实现更简洁的依赖注入.通过启用多个专门的事件处理函数, 坚持单一责任原则.第 1 步: 起跑线我们的冒险之旅从查看原始代码开始. 该代码库通过名为handleBlockEvent的函数和名为onEvent的事件处理函数来管理各种块事件. 让我们揭开原始代码的神秘面纱: open fun onEvent(event: Event) { // ... handleBlockEvent(engine, getBlockForEvents(), checkNotNull(assetsRepo.fontFamilies.value).getOrThrow()) } fun handleBlockEvent(engine: Engine, block: DesignBlock, fontFamilyMap: MapString, FontFamilyData, event: BlockEvent) { when (event) { BlockEvent.OnDelete - engine.delete(block) BlockEvent.OnBackward - engine.sendBackward(block) BlockEvent.OnDuplicate - engine.duplicate(block) BlockEvent.OnForward - engine.bringForward(block) BlockEvent.ToBack - engine.sendToBack(block) BlockEvent.ToFront - engine.bringToFront(block) BlockEvent.OnChangeFinish - engine.editor.addUndoStep() is BlockEvent.OnChangeBlendMode - onChangeBlendMode(engine, block, event.blendMode) is BlockEvent.OnChangeOpacity - engine.block.setOpacity(block, event.opacity) is BlockEvent.OnChangeFillColor - onChangeFillColor(engine, block, event.color) // and so on... } } sealed class BlockEvent : Event { object OnChangeFinish : BlockEvent object OnForward : BlockEvent object OnBackward : BlockEvent object OnDuplicate : BlockEvent object OnDelete : BlockEvent object ToFront : BlockEvent object ToBack : BlockEvent data class OnChangeBlendMode(val blendMode: BlendMode) : BlockEvent data class OnChangeOpacity(val opacity: Float) : BlockEvent data class OnChangeFillColor(val color: Color) : BlockEvent // and so on... } 要使用原始代码, 通常需要使用特定事件来调用onEvent函数: onEvent(BlockEvent.OnChangeFillColor(Color.RED)) 这将触发handleBlockEvent函数来处理手头的事件. 现在, 让我们开始第一次重构冒险. 第 2 步: 揭开HashMap和Payload的神秘面纱, 实现峰值性能在第一次重构中, 我们引入了值得信赖的HashMap来将每个事件类型映射到其对应的操作. 这一英勇之举消除了对复杂的when语句的需求, 使我们的代码更加高效. 我们还公布了一种Payload机制, 用于向事件处理程序传递重要数据. 请看重构后的代码: abstract class EventsHandlerPayloads( val fillPayload: (cache: Payloads) - Unit ) { abstract val payloadCache: Payloads private val eventMap = mutableMapOfKClassout Event, Payloads.(event: Event) - Unit() fun handleEvent(event: Event) { eventMap[event::class]?.let { it.invoke(payloadCache.also { fillPayload(it) }, event) } } operator fun EventType : Event set(event: KClassout EventType, lambda: Payloads.(event: EventType) - Unit) { eventMap[event] = lambda as Payloads.(event: Event) - Unit } } class BlockEventsHandler(fillPayload: (cache: BlockEventsHandler.Payloads) - Unit) : EventsHandlerBlockEventsHandler.Payloads(fillPayload) { class Payloads { lateinit var engine: Engine lateinit var block: DesignBlock lateinit var fontFamilyMap: MapString, FontFamilyData } override val payloadCache: Payloads = Payloads() init { it[BlockEvent.OnDelete::class] = { engine.delete(block) } it[BlockEvent.OnBackward::class] = { engine.sendBackward(block) } it[BlockEvent.OnDuplicate::class] = { engine.duplicate(block) } it[BlockEvent.OnForward::class] = { engine.bringForward(block) } it[BlockEvent.ToBack::class] = { engine.sendToBack(block) } it[BlockEvent.ToFront::class] = { engine.bringToFront(block) } it[BlockEvent.OnChangeFinish::class] = { engine.editor.addUndoStep() } it[BlockEvent.OnChangeBlendMode::class] = { onChangeBlendMode(engine, block, it.blendMode) } it[BlockEvent.OnChangeOpacity::class] = { engine.block.setOpacity(block, it.opacity) } it[BlockEvent.OnChangeFillColor::class] = { onChangeFillColor(engine, block, it.color) } // and so on... } } private val blockEventHandler = BlockEventsHandler { it.engine = engine it.block = getBlockForEvents() it.fontFamilyMap = checkNotNull(assetsRepo.fontFamilies.value).getOrThrow() } open fun onEvent(event: Event) { // ... blockEventHandler.handleEvent(event) } 性能提升通过利用HashMap的强大功能, 我们加快了事件处理的速度. 现在, 处理事件的时间复杂度仅为闪电般的(O(1)), 与繁琐的when语句的(O(n))时间复杂度相比, 这是一个巨大的进步. 我们的Payload机制增加了语法糖. 它使我们能够将所有必要的数据捆绑到一个对象中, 从而使我们的代码更易读, 更易维护. ?? 注意: 使用HashMap代替大型when语句可显著提高性能. 它可以将速度提高 40 到 150 倍. 不过, 解释细节将超出本文的范围. 因此, 我将在今后的博文中介绍它以及其他 Kotlin 性能难题. 重构后的代码仍然和以前一样简单: onEvent(BlockEvent.OnChangeFillColor(Color.RED)) 这仍然会触发BlockEventsHandler中的handleEvent方法, 然后根据事件类型执行相应的操作. BlockEvent本身是一个包含所有事件细节的数据对象, 它可以作为 lambda 参数. 关于Payload的说明Payload创建是一个动态 lambda 函数, 每次处理事件时都会执行. 这样可以确保所有不属于事件的变量都是最新的. 鉴于我们处理的是每个事件处理程序的单线程, 缓存Payload是完全安全的. 第 3 步: 使用 Infix 函数增加语法糖在下一步中, 我们将把语法的表现力和可读性提升到一个新的水平. 我们引入了一个名为to的infix函数, 让我们可以优雅地将事件类映射到相应的动作. 请看更新后的代码: abstract class EventsHandlerPayloads( val fillPayload: (cache: Payloads) - Unit ) { infix fun Payloads, EventType : Event KClassout EventType.to(lambda: Payloads.(event: EventType) - Unit) { eventMap[event] = lambda as Payloads.(event: Event) - Unit } // ... (rest of the code remains the same) } class BlockEventsHandler( manager: EventsManager, override val fillPayload: (cache: TextBlockEventsHandler) - Unit ) : EventsHandlerTextBlockEventsHandler(manager) { lateinit var engine: Engine lateinit var block: DesignBlock lateinit var fontFamilyMap: MapString, FontFamilyData init { BlockEvent.OnDelete::class to { engine.delete(block) } BlockEvent.OnBackward::class to { engine.sendBackward(block) } BlockEvent.OnDuplicate::class to { engine.duplicate(block) } BlockEvent.OnForward::class to { engine.bringForward(block) } BlockEvent.ToBack::class to { engine.sendToBack(block) } BlockEvent.ToFront::class to { engine.bringToFront(block) } BlockEvent.OnChangeFinish::class to { engine.editor.addUndoStep() } BlockEvent.OnChangeBlendMode::class to { onChangeBlendMode(engine, block, it.blendMode) } BlockEvent.OnChangeOpacity::class to { engine.block.setOpacity(block, it.opacity) } BlockEvent.OnChangeFillColor::class to { onChangeFillColor(engine, block, it.color) } // ... } } 语法优美和性能infix函数to的引入为语法增添了一抹亮色, 增强了代码的表现力, 使使用更加自然. 这使得每个事件的内容一目了然. 不用担心, 得益于我们值得信赖的HashMap, 性能仍然保持在极快的(O(1)). 语法的灵活性虽然这里使用了to关键字, 但你也可以用其他术语来代替它, 如handle,trigger或任何最适合你的上下文的术语. 灵活性就是本游戏的名字. 第 4 步: 使用内联函数以保持优雅然而, 这仍然不够完美, 因为::class破坏了阅读的流畅性. 因此, 让我们换一种方式. 让我们尝试引入一种更优雅的方法来注册事件. 让我们不再需要每次注册事件处理程序时都指定::class, 这将使我们的代码更加简洁易读. 这可以通过一个内联函数来实现, 该函数带有一个经过验证的类型参数, 可在运行时维护类引用. 为此, 我们用这个新的register函数扩展了EventsHandler类: class EventsHandler( register: EventsHandler.() - Unit, ) { inline fun reified EventType : BaseEvent register(noinline lambda: (event: EventType) - Unit) : Any { this[EventType::class] = lambda return lambda } // ... (rest of the code remains the same) } 新语法这就是使用新语法注册事件处理程序的效果: registerBlockEvent.OnChangeLineWidth { engine.block.setWidth(block, engine.block.getFrameWidth(block)) engine.block.setHeight(block, it.width) } 好多了, 是不是? 新的语法更加简洁, 消除了冗余, 而且是类型安全的, 因为重新定义的类型参数确保了事件类型在编译时和运行时都是已知的, 从而消除了不安全铸造的需要. 步骤 5: 将register提升为高亮显示的扩展函数为了提高代码的可读性, 我们将采取一个微妙但有效的步骤, 将register函数从一个EventsHandler类函数转换为一个EventsHandler扩展函数. 听起来很蠢! 为什么呢? 这个小改动通过语法高亮显示 Kotlin 扩展函数中的register关键字, 提高了代码的可读性. 这将使代码更加丰富多彩, 从而提高可读性. 更新了EventsHandler类EventsHandler类在很大程度上保持不变, 但register函数现在在类之外, 并转化为EventsHandler类的扩展函数`: class EventsHandler( register: EventsHandler.() - Unit, ) { // ... (rest of the code remains the same) } inline fun reified EventType : BaseEvent EventsHandler.register(noinline lambda: (event: EventType) - Unit) : Any { this[EventType::class] = lambda return lambda } 只需将register移出类,EventsHandler类的定义就会以独特的语法高亮显示出来. 这是一个巧妙的技巧, 不会影响运行时或编译期的性能, 因为这是一个内联操作. registerBlockEvent.OnChangeLineWidth { engine.block.setWidth(block, engine.block.getFrameWidth(block)) engine.block.setHeight(block, it.width) } 第 6 步: 使用委托属性消除lateinit变量现在, 是时候解决神秘的lateinit变量和有点复杂的fillPayload机制了. 让我们引入一种更简洁的方法, 使用委托属性和 lambda 函数来注入依赖关系. 让我们添加一个Inject类, 将普通的 lambda 包装成可委托的: class InjectType(private val inject: () - Type) { operator fun getValue(thisRef: Any?, property: KProperty): Type = inject() } 有了这种新发现的能力, 我们的事件处理程序代码变得更简洁, 更直观. 它采用了 Jetpack Compose 的声明式语法: fun EventsHandler.textBlockEvents( engine: () - Engine, block: () - DesignBlock, fontFamilyMap: () - MapString, FontFamilyData, ) { // Inject the dependencies val engine by Inject(engine) val block by Inject(block) val fontFamilyMap by Inject(fontFamilyMap) // Event handling logic here // ... } 每当访问其中一个变量时, lambda 就会被调用, 并始终获得当前变量. 此外, "payload"的创建也变得更加直接, 简洁和类型安全. 看起来有点像传递变量: private val eventHandler = EventsHandler { textBlockEvents ( engine = ::engine, block = ::getBlockForEvents, fontFamilyMap = { checkNotNull(assetsRepo.fontFamilies.value).getOrThrow() }, ) } 看起来感觉就像变戏法一样! 很酷吧? 第 7 步: 单一责任原则的多个事件处理在压轴部分, 我们将利用之前更改中新发现的灵活性来注册多个事件处理程序函数. 现在, 每个事件处理程序注册函数都有一个特定的主题, 完全符合单一责任原则(SRP). 增强的事件处理程序注册现在, 我们可以在同一个EventsHandler实例中注册多个事件处理程序函数. 每个函数都可以专门处理特定类型的事件, 从而使代码更加模块化和易于管理. 看看这个宏伟的设计: private val eventHandler = EventsHandler { cropEvents( engine = ::engine, block = ::getBlockForEvents, ) blockEvents ( engine = ::engine, block = ::getBlockForEvents, ) textBlockEvents ( engine = ::engine, block = ::getBlockForEvents, fontFamilyMap = { checkNotNull(assetsRepo.fontFamilies.value).getOrThrow() }, ) // ... } fun EventsHandler.blockEvents( engine: () - Engine, block: () - DesignBlock ) { val engine: Engine by Inject(engine) val block: DesignBlock by Inject(block) registerBlockEvent.OnDelete { engine.delete(block) } registerBlockEvent.OnBackward { engine.sendBackward(block) } registerBlockEvent.OnDuplicate { engine.duplicate(block) } registerBlockEvent.OnForward { engine.bringForward(block) } registerBlockEvent.ToBack { engine.sendToBack(block) } registerBlockEvent.ToFront { engine.bringToFront(block) } registerBlockEvent.OnChangeFinish { engine.editor.addUndoStep() } registerBlockEvent.OnChangeBlendMode { if (engine.block.getBlendMode(block) != it.blendMode) { engine.block.setBlendMode(block, it.blendMode) engine.editor.addUndoStep() } } registerBlockEvent.OnChangeOpacity { engine.block.setOpacity(block, it.opacity) } } fun EventsHandler.cropEvents( engine: () - Engine, block: () - DesignBlock ) { val engine: Engine by Inject(engine) val block: DesignBlock by Inject(block) // ... (event handling logic for cropping events) } fun EventsHandler.textBlockEvents( engine: () - Engine, block: () - DesignBlock, fontFamilyMap: () - MapString, FontFamilyData, ) { val engine by Inject(engine) val block by Inject(block) val fontFamilyMap by Inject(fontFamilyMap) // ... (event handling logic for text block events) } 在代码的触发及其 API 保持不变的同时, 也不需要传递额外的参数: open fun onEvent(event: Event) { eventHandler.handleEvent(event) } 最后的话在我们结束 Kotlin 代码重构之旅的时候, 我们已经揭开了增强性能和风格的神秘面纱. 通过采用HashMap, infix 函数和带有重定义类型参数的内联函数等技术, 我们将代码提升到了新的高度. 这样做的好处显而易见: 提高了效率, 可读性并遵循了单一责任原则. 有了这些工具, 你现在就可以开始自己的编码冒险, 将凌乱的代码变成优雅的杰作. Stay GOLD! 阅读原文

上一篇:2019-09-04_招聘 | 腾讯研究院法学实习生(研究助理) 下一篇:2022-05-18_多机位声画同步神器,TC-1今日上市!

TAG标签:

20
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设网站改版域名注册主机空间手机网站建设网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。
项目经理在线

相关阅读 更多>>

猜您喜欢更多>>

我们已经准备好了,你呢?
2022我们与您携手共赢,为您的企业营销保驾护航!

不达标就退款

高性价比建站

免费网站代备案

1对1原创设计服务

7×24小时售后支持

 

全国免费咨询:

13245491521

业务咨询:13245491521 / 13245491521

节假值班:13245491521()

联系地址:

Copyright © 2019-2025      ICP备案:沪ICP备19027192号-6 法律顾问:律师XXX支持

在线
客服

技术在线服务时间:9:00-20:00

在网站开发,您对接的直接是技术员,而非客服传话!

电话
咨询

13245491521
7*24小时客服热线

13245491521
项目经理手机

微信
咨询

加微信获取报价