本周的总结发得比较晚,花在打磨编程思考部分的时间比较多。
虽然最终版本也不是很满意,不过也算达到了阶段性交付的水平。
Go技巧 - 并发代码的单元测试
在Go
语言开发的过程中,我们或多或少会引入并发模式,常见的如go
、channel
、sync.WaitGroup
等。这些并发原语使用起来很方便,但常常会阻碍相关代码的单元测试,如依赖的channel
发生阻塞,mutex
被锁等,导致想验证的重要代码根本跑不到。
这里,我介绍一下自己的三个心得,用一句话概括,合理利用分层进行拆分,屏蔽并发逻辑。
没有分层的基础或工具库不在本次的讨论范围内,但也可以借鉴这里的思想。
多协程的逻辑交由上层控制
如下,原先的业务逻辑代码包括了两块处理逻辑:
1 | func Foo(){ |
由于逻辑B相关的代码无法验证,所以这个单元测试能做的很有限。这时,通过分层,我们将代码拆分为两部分:
1 | // 上层代码 - 使用基础的并发特性,没有必要做单元测试 |
此时的单元测试就变得直观了。
锁类逻辑由下层对象封装
业务代码常常会包含锁,这就导致很多函数中有大量的Lock
、UnLock
操作,容易在单元测试里验证一些逻辑时发生死锁。
1 | func Foo() { |
从抽象层面来说,业务逻辑的核心代码尽量减少锁这种 底层的并发原语,把它们放在业务逻辑里也很影响阅读体验。这时,将锁划分到下层会让代码逻辑更清晰:
1 | // 下层对象 |
从使用锁的角度来说,Lock
与UnLock
之间的逻辑应尽量短,所以很适合放在底层对象、交由它自行控制,也能缩短锁的影响范围。
同层抽离出核心控制函数
在Go
语言中,有一些并发特性、尤其以channel
为代表,很难通过分层解决。例如
1 | func Foo() { |
这里的channel
更多的是一种具有业务特性的并发控制,单独抽出一个 核心控制函数 就能提升整体的可读性与可测试性:
1 | // 核心控制函数 |
核心控制函数很难通过单元测试完整验证,更考验的是开发人员对Go
并发编程的基本功。
小结
以上三点是我在工作过程中的经验心得,帮助我解决了很多并发代码中的单元测试问题。它们不一定是最佳实践,但希望能对各位遇到相关问题时有一些启发。
编程思考 - 平台开发的三个阶段
如今, 平台开发 这个词已经广泛应用在程序员圈子内。在我看来,相关的开发者可以分为三个阶段:
- 初级阶段:根据用户的需求明细写过程性的CRUD,实现功能
- 中级阶段:以
OpenAPI
的方式向多业务、多用户提供能力 - 高级阶段:定制核心能力+复用通用能力
其中,很多人往往走到中级阶段后就阻塞不前了,并且心得意满、认为平台的已经处于最终形态。但是,平台如果长期处于中级阶段,相关弊端会随着时间推移越发凸显,例如:
- 没有技术壁垒 - 越是通用,越是普通,很容易被开源产品取代
- 调用方的使用成本高 - 开放接口往往透传各类参数,使用方成本高
- 无法贴合一线业务创造价值 - 平台以甲方自居,不去理解业务的使用场景
我见过许多工作了近10年仍处于中级阶段的工程师,他们的技术能力十分高超,但很难在职业发展的道路上再进一步,关键就是在于对平台的认知 - 平台的核心价值是靠 为业务创造的价值 来间接体现的,而不是靠 平台自身的能力 来直接评价。
工作生活 - 少给自己找借口的机会
年后,我完整地跑了2次十公里,过程中也有近10次跑了不到一半就放弃。我的跑步配速不快,理应每次都能达成目标,那为什么还有这么多次半途而废呢?
我回顾了这几次的经历,总结如下:一旦我想要中途放弃,借口总是能找到的;所以,在整个跑步的过程中,保持一个稳定、平和的心态,不要让放弃的想法有可趁之机。
Github: https://github.com/Junedayday/code_reading
Blog: http://junes.tech/
Bilibili: https://space.bilibili.com/293775192
公众号: golangcoding