lua 防御式编程辅助函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gneveek/article/details/78965564

防御式编程是提高软件质量技术的有益辅助手段。防御式编程的主要思想是:子程序应该不因传入错误数据而被破坏,哪怕是由其他子程序产生的错误数据。这种思想是将可能出现的错误造成的影响控制在有限的范围内。

以上是引用自百科的一段描述,在实际编码过程中,我们除了判断参数是否合法外,还会 assert 非法参数,以达到快速定位出错位置的目的。

一些示例

比如我们有一个 计算两个 number 之和的一个函数:

function add( a, b )
    return a + b
end

这个函数在我们传入正确参数的情况下,可以完成它的功能:

local c = add(3, 5) --返回8
local d = add(100, 200) --返回300

但假如我们只传入一个参数:

local e = add(3, nil)

则会报以下错误:
attempt to perform arithmetic on a nil value (local ‘b’)

所以我们要检查参数,下面是第二版:

function add( a, b )
    if type(a)=="number" and type(b)=="number" then
        return a + b
    end
end

这时再传 nil 进去(或者string/table/etc…),就不会报错了,函数什么也不会做,也没有返回值。

看起来已经可以了,但有时我们还有一些需要,就是希望我们写的函数,外部在调用时,都应该传入正确的参数,如果填了错误的参数,那肯定是调用法写出Bug了,这时我们希望能给出醒目的提醒,把问题扼杀在摇篮里。

这时就需要 assert 了。
下面是加了 assert 后的第三版:

-- 第三版:对错误参数零容忍
function add( a, b )
    assert(type(a)=="number" and type(b)=="number", "invalid params")
    return a + b
end

加 assert 在这种情况下看起来有些过于严格了,只是因为这个例子的原因而已,实际上在写真正的功能时,函数要比这个复杂的多,对错误参数的容忍程度也不同,我们先假定我们这个简单的 add 函数对错误参数是零容忍的。

这时候,再用非 number 参数调用 add,会立即触发 assert,中止程序的运行,开发者可以马上看到自己的错误用法。


第三版是对错误参数零容忍的,还是太严了,况且在线上运行的项目,是不能随便 assert 的,万一后续流程被中断,有可能引起很多Bug,比如游戏中一个玩家要买东西,钱已经扣了,在给玩家发放物品前 assert 了,导致花了钱没得到东西,那玩家肯定要投诉了。

所以我们有一个较温和的版本,只记录出错日志,而不 assert ,如下:

-- 第四版:唔,偶尔犯点错可以接受,只记过不开除
function add( a, b )
    if type(a)~="number" or type(b)~="number" then
        print("invalid param", tostring(a), tostring(b))
        return
    end

    return a + b
end

好了,以上第三版和第四版就是我们的所有需求了,只不过每次都要写这么多代码“防御”,也太麻烦了,有没有自动化的解决方案?

当然有了,会偷懒的程序员才是好的程序员,Let’s go!

辅助函数

-- 防御式编程 辅助函数

-- 第三版:对参数零容忍的辅助函数
function CHECK( condition )
    if not condition then
        Log.e("CHECK FAILED!!", tostring(condition))
        assert(false, "CHECK FAILED!!")
    end
end

-- 第四版:只记 Log 不 assert
function OK( condition )
    if not condition then
        Log.e("IF_OK!!")
        return false
    else
        return true
    end
end

-- 同样是第四版,只记 Log 的,但跟 OK 用法相反
function NOT( condition )
    if not condition then
        Log.e("IF_NOT!!")
        return true
    else
        return false
    end
end

-- 注:上述函数中的 Log.e,请换为自己项目中记录错误日志的函数。

好了,让我们用上面的辅助函数重写我们的 add 函数:

-- 第三版:对错误参数零容忍
function add( a, b )
    -- 一旦条件为假,就会 assert,并记录错误日志(含调用栈信息,方便查线上问题)
    CHECK(type(a)=="number" and type(b)=="number")
    return a + b
end

-- 第四版:只记 Log 不 assert
function add( a, b )
    -- 一但“不OK”,就会记录错误日志,但不会Assert
    -- OK条件返回 false, 所以 if 内的逻辑不会执行,跟直接用 if 判断效果一样
    if OK(type(a)=="number" and type(b)=="number") then
        return a + b
    end
end

-- 第四版也可以这么写
-- 但这样写极不自然,看着非常费劲,NOT 也不是这么用的
-- 关于OK和NOT,各自有各自的应用场景,如果现在不理解可以只用CHECK和OK,这两个用多了以后
-- 就知道NOT是干嘛的了 ;-)
function add( a, b )
    if NOT(type(a)~="number" or type(b)~="number") then
        return
    end

    return a + b
end
  1. CHECK 可以认为就是一个会打错误日志的 assert
  2. OKNOT 都是在判断的条件为 false 时,记录下错误日志,但并不会触发 assert。

在日常编码过程中,对于一些关键的、不希望为假的、一旦为假必为Bug的条件,可以使用 OK 和 NOT 判断,但不要滥用,普通的条件(true/false都为正常情况的)不建议使用。

END

猜你喜欢

转载自blog.csdn.net/gneveek/article/details/78965564