在初级程序员和高级程序员之间,有一条明显的分界线,就是代码质量。初级程序员写的代码,很多时候混乱不堪,看着就头疼;而高级程序员写的代码,看着就比较简洁,让人读着也不太费力。这中间的差别到底是什么呢?
表意清晰
代码首先是写给人看的。如果我们只凭潜意识或直觉写代码的话,代码也许能正确运行,但通常会不好理解。我们需要有意识地去标记那些具体的、有意义的概念。这里不仅包括变量的命名,也包括逻辑本身。有如下代码:
funcSetVip(userId uint32, days int) (string, error) { return cache.Redis.Set("vip:"+strconv.FormatInt(int64(userId), 10), time.Now().Unix() + 86400*int64(days), 0)}
以上代码是一个设置VIP的函数。看着貌似简洁的一行代码,读起来实际却稍显费力。我们尝试去做一些优化:funcSetVip(userId uint32, days int) (string, error) { now := time.Now().Unix() expiredTime := now + 86400*int64(days) cacheKey := "vip:" + strconv.FormatInt(int64(userId), 10) return cache.Redis.Set(cacheKey, expiredTime, 0)}
这里,我们引入了几个具体的概念:当前时间(now)、缓存键名(cacheKey)和过期时间(expiredTime)。很明显,这些具体概念的加入,让我们能更轻松地理解代码逻辑。人类天生不喜欢抽象、复杂的东西。越具体的概念,越容易被人接受,也越容易被人理解。逻辑结构化
当然,光是表意清晰肯定是不够的。有些项目,具体细节看着都不费力,但整体一看,却又混乱的让人不知所云。这大概率是因为,代码在整体上没有条理清晰的逻辑结构。提纲挈领,有着清晰的目录,几百万字的书籍也能很快了解个大概;全部平铺直叙,800字的短文也会让人匪夷所思。有如下代码:
funcSetVip(userId uint32, days uint32) error { // 获取现有用户的vip信息 cacheKey := "vip:" + strconv.FormatInt(int64(userId), 10) value, err := cache.Redis.Get(cacheKey) if err != nil { return err } // 当前时间 now := time.Now().Unix() // 用户无vip的处理逻辑 vipTime, _ := strconv.ParseInt(value, 10, 64) if vipTime == 0 { expiredTime := now + 86400*int64(days) return cache.Redis.Set(cacheKey, expiredTime, 0) } else { // 用户有vip还未过期的处理逻辑 if vipTime >= now { expiredTime := vipTime + 86400*int64(days) return cache.Redis.Set(cacheKey, expiredTime, 0) } else { // 用户有VIP但已经过期的处理逻辑 expiredTime := now + 86400*int64(days) return cache.Redis.Set(cacheKey, expiredTime, 0) } }}
以上代码增加了VIP提前续费的兼容。仔细阅读,倒也能明白大致含义。但读起来还是有些费力。这里,我们稍微做一些优化:
funcSetVip(userId uint32, days uint32) error { // 获取现有用户的vip信息 cacheKey := "vip:" + strconv.FormatInt(int64(userId), 10) value, err := cache.Redis.Get(cacheKey) if err != nil { return err } // 计算过期时间 vipTime, _ := strconv.ParseInt(value, 10, 64) expiredTime := ExpiredTime(vipTime, days) // 设置新的过期时间 return cache.Redis.Set(cacheKey, expiredTime, 0)}funcExpiredTime(vipTime int64, days uint32) int64 { // 当前时间 now := time.Now().Unix() // 未设置或已过期则从当前时间计算 if vipTime < now { return now + 86400*int64(days) } // 还未过期则从过期时间计算 return vipTime + 86400*int64(days)}
新代码重新梳理了原有逻辑,让其有了更加精细的层次化结构。很明显,可读性有了很大的提升。这里,也利用了函数的提前返回机制,让代码变得更加简洁。所以,实际编程中,我们需要挖掘更细节的逻辑结构,让程序更加清爽起来。
骨灰级原则
这里,还有一条骨灰级的编码准则,信仰一般遵守,才会让我们的代码更加优秀,这个准则就是:“一次只做一件事情”。无论是一个函数,还是一个代码块,都应该遵守这一原则。这会极大地降低代码的复杂度,让阅读和维护也变得极其方便。
写在后面的话
能比较好的做好以上几点,你写的代码,基本上算是比较专业了。当然,这也只是高质量代码的第一步,技术追求本就任重而道远。这里,还有一个大家容易起争论的地方,那就是可读性和性能的抉择,很多可读性的优化,其实并不是性能的极致最优解。这个如何决断呢?我们下篇讨论。
关注公众号,避免错过下回内容~
推荐阅读:
编程的本质是什么?
什么样的代码才是好代码?
大厂的接口设计能力到底行不行(微信小程序)
你选择Web框架的思路是不是有些问题?
编程神作《人月神话》