面临的问题
在 GBPartySocket
和 GBGlobalSocket
中,每增加一种业务 Action
(继承自一个基类 Action
),就要在 didHandleMessageData(messageData: )
方法中增加如下代码:
|
|
如此,经过不停迭代,上面的(didHandleMessageData(messageData:)
)代码就会变的越来越多,如下图非高亮区域所示(高亮区为第一期改进后的,下文后讲到)!
但碍于项目一直在紧锣密鼓中进行着迭代,虽然一直想着去改进,又没有合适的思路,于是一次又一次地跃跃欲试,又一次又一次地放弃!但,一切又在前两天出现了转机:马上中秋➕十一放假,节前的最后一次迭代提测后,有了点空闲时间(测试有问题就改一改,没问题就专心改进),一头扎进改进中,连下班都在想怎么改进(哈😄哈😄哈😄),经过2
次的调整,总算是有个满意的结果!
分析问题,找准痛点
想要解决问题,一定要弄清楚问题的根源(痛点)在哪!项目中的 Socket
推送信息结构,如下图所示,想要解析数据并做出响应,就一定要现根据 action
名,确定是哪种业务场景,即:根据 action
名确定要用哪个 Action
类!这一点,大家都明白,也处理了,但是处理的不是很满意:每增加一种业务,就增加上面提到的 isCurrentAction
方法判断,导致 Socket
中重复代码颇多。
有没有一种自动映射的方案呢?
我第一时间想到了反射:NSClassFromString
以及 Mirror
,经过调研,发现 NSClassFromString
还是有可能满足的,但 Mirror
就不行。
NSClassFromString
虽然可行,但又不行
这里说行,是因为从代码层面来说,确实可行!
但又不行,是因为 action
的名字没有按照统一的格式返回,毕竟项目一开始没有考虑过这个(App
端没有考虑过、后端也没有考虑过),如今为了 App
端( iOS
端),让后台再去把所有的 action
名字按照统一的格式整理一下,“不可能,绝对不可能”!
|
|
想要的
action
名
1 2 3 4 5 6 7
/// 房间斗地主业务的action名 "FightLandlord.start_mode.notice" "FightLandlord.close_mode.notice" /// 开黑CP业务的action名 "EscortCp.success.notice" "EscortCp.upgrade.notice"
如果后台推送的 action
名,按照想要的统一格式,我们就可以如下处理:
|
|
解决问题
第一期改进
捋一捋思路:
- 一一匹配:根据
action
名确定要用哪个Handler
类,始终是重点,避不开要一一去匹配! action
名字符串只定义一次:为了方便匹配 action 名,可采用let
方式的action
名,或者枚举类型的action
名,这里选择后者。- 业务独立:全局用一个
Handler
,后台返回的所有action
名放到一个类里,无论let
方式的action
名,或者枚举类型的action
名,都不重要了,但又回到了根本问题上,Handler
又变的无比复杂! - 各个自定义
Handler
类有哪些共同性:处理各自独立的业务逻辑 鉴于该思路的思考和不停地尝试,第一期改进以枚举嵌套 + 协议落地:
1.1 声明协议 + 定义枚举
PBSocketHandlerProtocol
:各种Handler
的handle
推送信息的协议,由各个业务的Handler
类遵循并实现PBSocketGetHandlerProtocol
:获取各自业务的具体Handler
实例HandlerProtocol
:PBSocketGetHandlerProtocol
和CaseIterable
(其实没用到) 的别名
1.2 创建领导型 Handler
和 员工型 Handler
PBSocketHandler
和PBBilliardsHandler
:前者相当于领导,后者相当于员工,当推送消息来了,领导根据action
名,找到具体员工,让该员工去处理。
1.3 编写代码
- 创建
PBSocketNoticeScene
枚举(见上图),并在其extension
中创建各种细分业务的枚举,这里以桌球游戏业务为例,即:Billiards
枚举,并遵循HandlerProtocol
协议,handler
的获取只有在真正需要的时候才去创建,见下图: - 对具体业务
Action
类改造:修改GBBilliardsAction
(继承自GBSocketAction
) 为PBBilliardsHandler
(遵守PBSocketHandlerProtocol
) 老版本的代码(仅供参考):
第二期改进
在第一期改进中,还有令人不满意的问题:
GBPartySocket
中的didHandleMessageData(messageData: )
里的众多XXAction
的isCurrentAction:
,并没有得到很好的改善,只是转移到了所谓的领导型PBSocketHandler
中;PBSocketHandler
中改动太多,每增加一种业务的同时需要新增一个匹配判断和一个业务case
,依然比较繁琐,并且这种方案,引入了中间商(也即:PBSocketHandler
,所谓的领导)
那如何更进一步优化呢?经过反复地思考与尝试、反复地尝试与思考,本着一个能简单绝不麻烦➕不重复的原则,迎来了第二期的改进,终令人满意!这里迫不及待地想展示这一期的结果:
2.1 首先,PBPartySocket
中的 didHandleMessageData(messageData:)
方法,只剩下 2
行代码:
2.2 声明SocketActProtocol
协议,用于具体业务 Action
处理业务
说明:该协议,不仅具体的业务
Action
遵循,PBPartySocket
和PBGlobalSocket
也遵循(而这里也是个巧妙之处)。
SocketActProtocol
|
|
所谓的巧妙分析:在第一期改进中提到了领导型 Handler
和员工型 Handler
,到了第二期改进时,我发现领导型 Handler
成了讨人厌的“中间商”,所以当 PBPartySocket
或者 PBGlobalSocket
收到后台 Socket
推送时,也可以通过 SocketActProtocol
的 act(_ ,data)
方法处理,而这相比于具体的业务 Action
的 act(_ ,data)
方法处理(这个也,具体见下文),只是里面的实现方式不一样!
2.3 声明 SocketActionable
协议,用于 Socket
子类(如:GBPartySocket
或者 GBGlobalSocket
)获取自己所有的业务 Action
类型,以及让具体的业务 Action
实例执行 Socket
推送信息处理
SocketActionable
|
|
上文提到 PBPartySocket
也遵循 SocketActProtocol
,对于 act(_, data:)
方法的实现,可以用 SocketActionable
的 extension
默认实现,如此下来,后续如果有新的业务需求,只需要定义对应的业务 Action
,并在 GBPartySocket
或者 GBPartySocket
中的 actions
的 getter
属性中新增如下代码即可( 总结:这里只需要增加1
行代码):
|
|
2.4 声明 SocketActionProtocol
协议,用于获取 action
名对应的业务 Action
获取到
names
就可以通过SocketActionProtocol
的扩展实现isHit
方法
SocketActionProtocol
|
|
业务 Action
的核心当然还是要处理 Socket
推送信息,所以业务 Action
要同时遵守 SocketActionProtocol
和 SocketActProtocol
,这里采用别名形式加以整理:
SocketAction
|
|
2.5 声明具体业务 Action
,并实现相关协议
示例 EscortCPAction
- 定义
EscortCPAction
结构体,并嵌套EscortCP: String, CaseIterable
和RoomQuality: String, CaseIterable
枚举 (这里也可以合并为一个枚举,但个人更喜欢各自业务细分)
|
|
- 扩展
EscortCPAction
,并遵循SocketAction
- ① 实现
SocketActionProtocol
中的names
和action
- ② 实现
SocketActProtocol
中的act(_, data:)
|
|
|
|
总结
- 创建业务
Action
类,并遵守并实现SocketAction
协议 - 在对应的
Socket
类中的SocketActionable
协议的中actions
新增上面的Action
- 在业务
Action
类中的act(_, data: )
方法中处理具体业务 最后,整理里一下大概流程,如下图: