0x01
这个系列有一段时间没更新了,最近在业务编码过程中,有一些想法,于是把其中的点滴记录下来,跟大家分享下。
0x02 callback
在客户端开发中,日常会遇到很多 callback, delegate 之类的回调,所以当转过来到 dart 开发也可能会理所当然的习以为常了。我们来看下项目中这种情况:
先以 OC 层的代码说一下上下文:
Thunder的进入房间是异步的,通过 joinRoom
触发,然后通过 ThunderEventDelegate
的 onJoinRoomSuccess
来回调结果。1
- (void)thunderEngine:(ThunderEngine *)engine onJoinRoomSuccess:(NSString *)room withUid:(NSString *)uid elapsed:(NSInteger)elapsed
同样,退房间的结果也是异步的,通过 leaveRoom
触发,然后通过 ThunderEventDelegate
的 onLeaveRoomWithStats
1
- (void)thunderEngine:(ThunderEngine *)engine onLeaveRoomWithStats:(ThunderRtcRoomStats * _Nonnull)stats
无可厚非,在客户端开发中,这种 delegete 回调方式是很正常不过的,因为这个ThunderEventDelegate
的存在, 与直接在函数里面加callback 相比,这样让业务层可以自主去赋值,自定义去对象去处理这些回调,给业务层提供解耦的机会,就不用把所有逻辑都堆在同一个文件里面。
以上角度是咱们在 OC 开发者的角度来看的。
继续以 Thunder 进退房间为例子,业务日常开发的逻辑,往往是实现比当初设计要复杂得多的,比如:业务存在某种场景,需要先退房间A,再进房间B。
由于退房间的过程是异步的,如果继续以客户端的思维去编码的话,就必须把进房间的流程,堆在 onLeaveRoomWithStats
退房间完成的回调里面了,而其他业务正常的退房间,又不用进房间的,于是乎,onLeaveRoomWithStats
里面的逻辑会慢慢多起来。
1 | //退房间A |
1 | //回调 |
当新人来读代码的时候,要了解 joinRoomB logic 的时候,又得跳到 onJoinRoomSuccess
看,在进入房间后到底做了什么
1 | - (void)thunderEngine:(ThunderEngine *)engine onLeaveRoomWithStats:(ThunderRtcRoomStats * _Nonnull)stats { |
说实话,这样的代码阅读起来由于跨度比较大(累)的,逻辑分离的比较厉害。
当我们转到 dart 开发的时候,可以想一下,这种回调的方式是否是 dart 语言的最佳实践呢?
0x03 await kills callback
dart 因为 await 的存在,我们可以把很多回调式的写法,转换成流式。继续用刚刚进退房间那个做例子,来看看dart的写法可以简化到一种怎么样的程度:
1 | //leaveRoom A |
没错,就是这么简单,3行搞定···,业务上层完全脱离了回调来编程了,逻辑很紧凑,阅读起来会很舒服。
这个 koThunder 是我们业务对 FlutterThunder 的隔离层,在里面,我们对回调的方式进行了处理。
以 leaveRoom 为例吧:
1 | //KoThunder |
joinRoom 的处理操作也同理,这里不重复了。
原理很简单,无非就是用 Completer 来处理回调,向外统一暴露一个 Future 的东西就好了。这样写,个人觉得十分的 dart 化。把复杂留给自己,简单暴露给调用方,这样的代码,让业务上层的逻辑更容易阅读,更容易维护。
0x04 总结
我们在公司开发 flutter plugin 也有一段时间了,但很多时候都是按葫芦画瓢,对底层sdk 做一层dart层的封装, 能调用就完事了,并没有真正地把 dart 的语言特性发挥出来,这个是做得很不够的地方。唯有真正深入业务,理解业务的复杂,并把相关语言特性发挥出来,才能写出更好用的基础组件。