突破代码结构

突破代码结构
@wujunguo 2017.8.18



昨天开始修改代码风格,大佬提点后,做了几点修改(Lua):
1、访问函数外表表有开销,多次访问的使用local。如果是赋值,处理完这个local后再一次性赋值回去。
2、减少if-else缩进。
    (1)在函数开头使用排除法,return所有出错或者无效的情况
    (2)使用if-return结构减少一个else缩进(Lua的for中无法用if-return代替continue怎么办?求解!)
    (3)先写if一级条件,再写子条件,逐级按顺序写,对于多重的elseif/&&/||/if-else很有用
    (4)情况允许下,常用条件先写,少用后写
    (5)用额外的函数分块合并if语句
    (6)for中的if尽量等到for结束后再处理,可增加临时变量将for中的信息传到for后面的if
3、掌握增删空格、空行的意义。
    (1)逗号后面必须要空格,或者是换行。其他比如等号、运算符、比较符等后面都要加空格
    (2)每个换行都有含义,空一行和空两行也是不同含义,删除无意义的空行,一段描述一个意思的代码后面增加空行
4、索引统一为使用 . 而不用 [ ],只有在索引是数字或者带有特殊符号时才用[ ]。
5、只需要打印跟外部调用者有关系的调试信息,内部正常的执行情况无需打印,错误信息除外。
6、文件开头写清楚用法简介和流程,外部接口签名处再描述每个接口的具体用法和注意事项。
7、函数名尽量使用 “动词 + 宾语” 的形式,直观地说明了这个函数干了什么。函数对应的注释也应该是 动词+宾语 这种形式。(在面向过程比较实用,不清楚在面向对象是否合适)
8、一段内聚性比较强或者比较常用的代码,分离出来作为一个独立的函数。
9、配置文件或者外部接口、外部变量,必须要用注释逐条信息解释清楚,并且为每条信息添加一个正确的例子。



今天改到C#层代码时,发现不仅是风格,代码结构都很有问题。
(大佬的理解)代码结构有问题:不需要知道写了什么,一眼扫过去,不舒服、不清晰、不想看、会烦躁。韩红看了想打人,就是有问题。

最初想到每个检测都要分为不同btn,每个act又要分为不同act,打算为指定的一组“act+btns”设置一个函数。
这种 n*m 的分情况处理,相乘一下 (act*btns) 需要写十多个,放弃了。

于是决定选其中一种,为其每个具体情况只列出一次(这里选择了btn),而另外一种在每个btn里面都列出一次。这样子就只需要n个函数而已,然后每个函数再写m个情况就好了。

第一次的代码结构:
rtn check(act,btn1,btn2,...){
    return checkExcute(act,btn1,...)
}

rtn checkExcute(act,btn1,btn2,...){
    rtn = null
    if(rtn = hasExcuteBtn1(act,btn1) != null)
        return rtn
    if(rtn = hasExcuteBtn2(act,btn2) != null)
        return rtn
    ...
}

-- n个函数,每个函数m个case
rtn hasExcuteBtn1(act,btn){
    switch(act){
        case 1: 1 dosomething
        case 2: 2 dosomething
        ...
    }
    dosomething
    return rtn
}
rtn hasExcuteBtn1(act,btn){
    switch(act){
        case 1: 3 dosomething
        case 2: 4 dosomething
        ...
    }
    dosomething
    return rtn
}
...




后来问题出在最后hasExcuteBtn那部分,只要新加一种就要加一个新的函数。
而且函数的内容很相似的,一个的逻辑需要增加一行代码或一个子情况,全部函数都要翻一遍增加一行。
另外由于子情况多,每个函数都很长,每次新增一个,版面瞬间暴涨一两百行。

吃晚饭的时候忽然想到,最初是n*m种情况,减少了m子情况的细分(也就是砍掉后面的乘数),合并成了n种。那现在n种情况,我再减少n的细分(n = 1 * n),就变成1种啦!
合并了btn情况后,反之亦然,act的m*n种情况,也可以合并成m种!
这样就只有1个btn函数,函数里面列出m中act情况!
btn...抽象为一个Btn!

提问:为何act的m种不继续合并成1种?
目前想法?:合并的话是为了代码更清晰,修改更灵活,n*m这么多情况,还是要一一作出处理的。合并成一种就变成了1*1,也就是1个函数里只有1种抽象处理,那么这种抽象处理还是要分m种情况处理一次的。如果一直1*1的话,没法完成n*m种情况的处理,没意义。

第二次的代码结构:
rtn check(act,btn1,btn2,...){
    return checkExcute(act,btn1,...)
}

rtn checkExcute(act,btn1,btn2,...){
    rtn = null
    if(rtn = hasExcuteBtn(act,btnType,btn1) != null)
        return rtn
    if(rtn = hasExcuteBtn(act,btnType,btn2) != null)
        return rtn
    ...
}

-- 1个函数,每个函数m个case
rtn hasExcuteBtn(act,btnType,Btn){
    switch(act){
        case 1: handleAct1(btnType,Btn) dosomething
        case 2: handleAct2(btnType,Btn) dosomething
        ...
    }
    dosomething
    return rtn
}

-- m个函数,每个函数n个case
-- 本来2m个函数,每个函数重载了一次。后来想到上面的Btn抽象成1个,这里也抽象成1个,具体再在函数中转换!
handleAct1(btnType,Btn){
    switch(btnType){
        case a:1
        case b:3
        ...
    }
}
handleAct2(btnType,Btn){
    switch(btnType){
        case a:2
        case b:4
        ...
    }
}
...



一开始的思路分得太细,虽然合并了一层,但是应该再往上抽象,先不要管具体的子情况,就当做是1种情况来思考代码结构,整个结构会清晰简单很多。

代码分多一两个层级,每个函数可以不用那么挤。
主要的是btn是大的判断逻辑,而act只是一种判断逻辑,比较轻巧。大逻辑为主,小逻辑分离,这样看起来思路比较清晰,减少大逻辑重复代码。

最开始的思考方式:检查n种btn,每种btn分为m种act,如此应该列出n*m个函数。
现在的思考方式:检查btn,btn中检查act,返回检查结果。整个结构流程已经明了。

提问:对比第一次的代码,函数反而多了,"m个函数,每个函数n个case"和"m个函数,每个函数n个case"都是乘法个数,没区别。

目前想法?:看起来确实没什么差别,主要在于少写了重复的dosomething,这样看起来hasExcuteBtn流程更清晰。而且本来打算handleBtn的case需要的btn类型有两种,要重载,数量反而还多了一倍,幸亏后来抽象了。


刚才开始茅塞顿开,思路可能未完整,欢迎评论提点,我会及时吸取意见!

你可能感兴趣的:(突破代码结构)