写给所有程序员_你的逻辑可以更简洁易读吗?

1.if层次过多。

举一个很有趣的例子,假如世界上只有两种烤鸭,在北京的叫做北京烤鸭,不在北京的叫做非北京烤鸭,写烂代码的程序员思路如下:

如果烤鸭不在地球,它一定不是北京烤鸭;
如果烤鸭不在陆地,它一定不是北京烤鸭;
如果烤鸭不在亚洲,它一定不是北京烤鸭;
如果烤鸭不在中国,它一定不是北京烤鸭;
如果烤鸭不在北京,它一定不是北京烤鸭;
否则,这是北京烤鸭。

伪代码如下:

    if(!烤鸭在地球){
        输出非北京烤鸭。
    }else{
        if(!烤鸭在陆地){
            输出非北京烤鸭。
        }else{
            if(!烤鸭在亚洲){
                输出非北京烤鸭。
            }else{
                if(!烤鸭在亚洲){
                    输出非北京烤鸭。
                }else{
                    if(!烤鸭在中国){
                        输出非北京烤鸭。
                    }else{
                        if(!烤鸭在北京){
                            输出非北京烤鸭。
                        }else{
                            输出北京烤鸭。
                        }
                    }
                }
            }
        }
    }

好把,如果真有哪个程序员是这样写代码的,那么我真的很想一巴掌把它拍到火星去。不要这样嵌套好吗?就算你真的是这么想,也可以这样写:

if(!烤鸭在地球){
    输出非北京烤鸭。
}else if(!烤鸭在陆地){
    输出非北京烤鸭。
}else if(!烤鸭在亚洲){
    输出非北京烤鸭。
}else if(!烤鸭在中国){
    输出非北京烤鸭。
}else if(!烤鸭在北京){
    输出非北京烤鸭。
}else{
    输出北京烤鸭。
}

这个时候你可以发现原来前四个条件的内容都是相同的,那么可以这样写:

if(!烤鸭在地球
    ||!烤鸭在陆地
    ||!烤鸭在亚洲
    ||!烤鸭在中国
    ||!烤鸭在北京){
    输出非北京烤鸭。
}else{
    输出北京烤鸭。
}

当你发现前面if的条件过分臃肿的时候,可以考虑反过来会不会更容易
最终变成:

if(烤鸭在北京){
    输出北京烤鸭。
}else{
    输出非北京烤鸭。
}

可见,逻辑简化可弥补思维的不足。
这里有两个公式:
公式1:

if(条件1){
    //做事情A
}else if(条件2){
    //做事情A
}

可以转换为:

if(条件1 || 条件2){
    //做事情A
}
if(条件1){
    if(条件2){
        //做事情A
    }
}

可以转换为:

if(条件1 && 条件2){
    //做事情A
}

如果条件太多,且实在不知道怎么合并,可以把条件单独放到一个函数:

public void doSomething(){
    if(isRoastDuckNotInBeijing()){
    输出非北京烤鸭。
    }else{
        输出北京烤鸭。
    }
}

public boolean isRoastDuckNotInBeijing(){
    return !烤鸭在地球
        ||!烤鸭在陆地
        ||!烤鸭在亚洲
        ||!烤鸭在中国
        ||!烤鸭在北京;
}

注意,如果条件可读性强:比如sex == “man”就不要把条件放到单独函数。

2.学会使用return降低层次

public void doSomething(){
    if(firstNum!=0){
        if(secondNum!=0){
            //doSomething
        }
   }
}

如果改为:

public void doSomething(){
    if(firstNum == 0)
        return;
    if(secondNum == 0)
        return;
    //doSomething
}

看起来好多了。

3.学会使用函数分割代码

我是在上大学的时候学会写代码的。有一点解不开的疑惑:为什么要写函数?我把代码从头写到尾逻辑不是更加通顺吗?每个教代码的老师,总是在开始的时候给我们讲什么是变量,什么是函数,怎么用?我好想说一句:喂,我还没决定要用函数呢!

讲这部分之前,我先讲讲这个为什么要写函数,讲为什么要写函数前,我不得不提提二分查找法

举个例子:
有十六张扑克牌按照编号从小的顺序排列,我让你找出编号排名第11的,你怎么找呢?

按照顺序一本本找,你要找11次,如果我找的是最后一张,需要找16次(当然这里我们不讨论倒着找)

使用二分查找:分成一半,前面是1-8,后面是9-16,然后再分成一半9-12和13-16,然后再分成一半9-10和11-12,然后我找到了。我找了4次。发现了吗?速度比顺序查找要快,而且我最多只需要找4次。当查找的范围越大,二分查找相对顺序查找的优势就越明显,平均速度也更快。

好了,懂了二分查找,我们再来弄清楚为什么要写函数
举个例子:假设我要写100行代码,伪代码如下:

public void 写100行代码(){
    写1-50行代码();
    写51-100行代码();
}

public void 写1-50行代码(){
    写1-25行代码();
    写26-50行代码();
}

public void 写51-100行代码(){
    写51-75行代码();
    写75-100行代码();
}

这样如果我找写第70行代码写什么,只要使用二分法的原理:先找写100行代码(),然后找写51-100行代码();以此类推。

所以我们写函数的目的就在此:把代码分割成小的部分,更容易查找。

这样做有个前提:就是你的函数名必须有意义并且表达准确,否则就更浪费时间,如上文的写1-50行代码()写51-100行代码()函数名调换,内容不变,识别起来难度就会变大,你先找写1-50行代码(),发现它其实是写后面50行代码,这会让你在逻辑上纠结一段时间,所以再次强调:函数名一定要有意义

另外还有一点,分割项目不要一次分的太细比如上面的例子,我也可以这样写(为了显示效果,我没写省略号,就一行行写了):

public void 写100行代码(){
    写1-5行代码();
    写6-10行代码();
    写11-15行代码();
    写16-20行代码();
    写21-25行代码();
    写26-30行代码();
    写31-35行代码();
    写35-40行代码();
    写41-45行代码();
    写46-50行代码();
    写51-55行代码();
    写56-60行代码();
    写61-65行代码();
    写66-70行代码();
    写71-75行代码();
    写76-80行代码();
    写81-85行代码();
    写86-90行代码();
    写91-95行代码();
    写96-100行代码();
}

发现了吗?这段代码的可读性明显变差了。所以一般情况下这种分割函数(里面每一句都调用其他函数),分割函数代码不要超过10句,最好不要超过5句

和分割函数相对应的,还有一种我称作逻辑函数,这种函数都是做具体的工作,一般不要超过20行,如下:

public void 写1-5行代码(){
    写第一行
    写第二行
    写第三行
    写第四行
    写第五行
}

特殊情况1:有一堆的参数赋值/传递:
比如录入个人信息:有身高,体重,爱好,优点,缺点,性别,父名,母名…这种可能会有很多数据,可能远远超过20行,那么,就把这部分代码单独分割:

public void outputUserInfo(){
    System.out.println(getUserInfo());
}

public UserInfo getUserInfo(){
    UserInfo userInfo  = new UserInfo();
    userInfo.setHigh(10);
    userInfo.setHeavy(1000);
    userInfo.setHobby("泡妞");
    userInfo.setAdvantage("很帅");
    userInfo.setDisadvantage("我是世界上最帅的!");
    userInfo.setFatherName("爸爸");
    userInfo.setMotherName("妈妈");
    //...此处省略万字
    return userInfo;
}

大概是这样,其实真的无法分割吗?并没有这样说法。上面的内容可分为:身体信息,偏好信息和亲友信息,于是上面的一个UserInfo就可以分割为BodyInfo,LoveInfo和RelativesInfo,于是又可以保持在20行之内了
ヾ(◍°∇°◍)ノ゙

特殊情况2:逻辑分支,回调和异常处理
逻辑分支例子:

if(!烤鸭在地球){
    输出非北京烤鸭。
    输出当前地点。
}else if(!烤鸭在陆地){
    输出非北京烤鸭。
    输出当前地点。
}else if(!烤鸭在亚洲){
    输出非北京烤鸭。
    输出当前地点。
}else if(!烤鸭在中国){
    输出非北京烤鸭。
    输出当前地点。
}else if(!烤鸭在北京){
    输出非北京烤鸭。
    输出当前地点。
}else{
    输出北京烤鸭。
}

回调例子:

public void clickButton(){
    button.setOnClickListener(
        new OnClickListener(){
            @Overriade
            public void onClick(View v){
                输出当前地点。
                输出北京烤鸭。
            }
    }
   );
}

异常处理例子:

public void methodName(){
    try{
        输出北京烤鸭。
        输出当前地点。
    }catch(Exception e){
        e.printStackTrace();
    }
}

上面三种情况:选择分支,回调和异常处理都会导致代码臃肿,可读性变差,我通常都会把里面的部分单独列到新的:
第一种:
合成为:输出结果(boolean isRoastDuckInBeijing,String address)
因为条件分支往往好起名字,所以我通常会这样写

第二种:
clickButtonEvent(){}
因为我不能保证除了输出北京烤鸭信息外是否会做些别的什么,通常这部分的修改频率较高

第三种:
public void methodNameThrowException() throw Exception{}
通常我会在原名称后加上ThrowException提醒自己抛出异常,这样既显示了区别也降低层次,看起来更舒服了:

public void methodName(){
    try{
        methodNameThrowException();
    }catch(Exception e){
        e.printStackTrace();
    }
}

public void methodNameThrowException() throw Exception{
    输出北京烤鸭。
    输出当前地点。
}

总结:

1.学会用if…else if代替多层次if…else
2.学会用逻辑运算符&&和||代替多层if和多分支if … else if
3.学会用return降低层次
4.函数参数要尽量少,太多的把它合成一个类
5.函数名要易读
6.函数要尽量短小
7.有多层次或多分支的,使用新的函数代替代码块。

你可能感兴趣的:(归纳整理,程序员)