有一天的清晨,老A早早的来到了办公室,决定进行一次code review,当然,对象必然是入职不久的小C。小C打开svn,展示了一段最近写的代码.

function ProcessItem()

assert(gItem) //断言道具表存在

assert(gEquip) //断言装备表存在

--其他代码

end

function ProcessItemEquip()

if not gItem then

return

end

if not gEquip then

return

end

//其他代码

end

这2个函数的意图很明显都是在函数入口处对配置不存在的时候进行的处理。

那么我们首先应该清楚在逻辑上,这2个配置表可能是为nil的,也就是空的。那么我们究竟应该选择哪一种写法呢?

先讨论一个问题,什么时候应该用assert进行断言,什么时候应该用return。

a.在逻辑上面允许发生的情况下,我们应该用return。

b.如果是逻辑上认为不应该发生的,我们应该用断言。

在上面的问题上,如果在设计上在调用ProcessItemEquip可能存在gItem为空的情况,那么我们在这边应该用return,如果在设计上,ProcessItem调用的时候gItem应该存在了,那么应该用assert。当然,从这个问题延伸的是更深的一个设计问题,我们应该把逻辑设计成在一个函数调用某个配置的时候,这个配置就是存在的,这样会大大减轻后续代码的各种判断。从这个角度也可以说明,我们应该将设计设计为更适合使用assert这样的环境。

讨论下面一个场景,写一个引用计数的2个接口

self.nRef = 0;

function incRef()

Self.nRef = Self.nRef + 1

End

下面是2种写引用计数的写法,哪种更正确?

function decRef()

self.nRef = self.nRef – 1

assert(self.nRef >= 0)

end

function decRef()

self.nRef = self.nRef – 1

if self.nRef <= 0 then

end

end

第一种是断言,第二种是容错。

小C觉得使用第二种,就没有必要关注外界用错导致引用计数被多减的情况,方便了外界逻辑层的调用。那么为什么我们还是推崇第一种写法,因为第一种写法是在问题发生的时候第一时间把错误断住了,它要求逻辑调用者必须找到引发断言的根本性问题。

那么我们经常在什么时候会写return这种兼容的代码?比如说一个网络消息过来,检测道具数量够不够,第一次协议过来是够的,第二次协议过来就不够了。那么我们在检测道具不足的时候用return而不是assert。那你可能会说客户端也加检测就可以assert了。考虑到第一条协议可能还没返回新的数据给客户端的时候第二条协议也过来(数据层我们统一以服务器为准),这时候客户端是拦不住的,所以这种情况我们使用return。

f7bf63eb296629fe08831a03fbb89af9.png
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐