面临的问题
在 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: )方法中处理具体业务 最后,整理里一下大概流程,如下图: