美文网首页
几个编程准则

几个编程准则

作者: 神之试炼者 | 来源:发表于2020-01-18 22:19 被阅读0次

    参考书籍: 《代码整洁之道》,《重构》,《程序员修炼之道》

    这三本书里有许多优雅的编程建议
    总结几个我认为日常使用最多的,供参考

    目录:

    • 命名清晰
    • 一个函数只做一件事
    • 合理注释
    • 处理好异常逻辑
    • 边界清晰
    • 不要容忍“破窗户”

    准则一: 命名清晰

    回想一下:你大多时候是在读代码,还是在写代码?

    有经常用下面单词命名变量, 类名吗?

    obj, flag, list, list1, map, xmap, tmp, params, info, json, result, i, j, k, x, y, z...
    Manager, Data, Info, Processor

    比如下面这个函数:

    public List<JSONObject> getInfos(){
        List<JSONObject> list = getList();
        List<JSONObject> list1 = new ArrayList();
        for(JSONObject item: list){ 
            if(item.get("age")< 10){
                   list1.add(item);
            }
        }
        return list1
    }
    

    info是什么东西的info?
    list是什么东西的list?
    list1又是什么?
    item是什么?
    “小于数字10”代表什么?
    JSONObject里面有什么?

    下面是不是更好一些?

    public List<User> getChildren():
        List<User> users = getUserListFromDB();
        children = new ArrayList<User>();
        for(User user : users ){
            if(user.isChilden()){
                   children.add(user)
            }
        }
        return children;
    }
    class User {
        private static final int childen_age_limit = 10; 
        String age;
        boolean isChildren(){
              if(this.getAge()<childen_age_limit) return true;
              return false;
        }
    }
    

    【这块代码能写的更好,这里不赘述】

    切记:

    1. 要清晰, 不要模糊
    1. 要准确: 能叫“苹果”的时候, 别起名叫“对象”
    2. 不要“创造”单词:YYMMDD你打算怎么读它?peop是什么的缩写?(people)
    1. 简洁更重要
    1. 不要废话:能叫Product, 就别叫:ProductInfo, ProductData,这是无意义后缀
    2. 不要重复:Api模块里的类,没必要带前缀ApiUser,ApiService,成员变量也没必要加m前缀,这是无意义重复

    【更多命名规范请参考:《重构》《代码整洁之道》这类书】


    准则二: 一个函数只做一件事

    函数有三大规则:

    1. 要短小
    2. 要更短小
    3. 还要更短小

    比如"小明起床去上学"的函数

    可能缺乏经验的程序员会这么写:

    def 小明上学(){
          小明睁眼;
          小明看闹钟;
          if  时间还早:
              return;//小明接着睡觉;
          else:
                 if 剩半小时:
                         小明起床;
                         小明穿裤子;
                         小明穿上衣;
                         小明刷牙;
                         小明洗脸;
                         小明吃煎饼;
                         小明喝豆浆;
                         小明跑着去上学;
                 else if 剩十分钟:
                         小明起床;
                         小明穿衣;
                         小明跑着去上学;
    }
    

    如果逻辑更复杂, 他能把这个函数写的和解八元七次方程一样长

    如何做到“短小”?

    拆函数!

    怎么拆?

    记住一句话:“每个函数一个抽象层级”

    优化后:

    def 小明上学(){   //第一层抽象
          if 到起床时间没() == false:
             return;
          if 剩半小时:
             缓慢起床();
          else if 剩十分钟:
             快速起床();
    }
    def 到起床时间没(){ //第二层抽象
          小明睁眼;
          小明看闹钟;
          if 时间还早:
               return false
          else:
               return true
    }
    def 缓慢起床(){   //第二层抽象
          小明穿衣();
          小明洗漱();
          小明吃早餐();
          小明去上学();
    }
    def 快速起床(){   //第二层抽象
          小明穿衣();
          小明去上学();
    }
    def 小明穿衣(){   //第三层抽象, 底层细节,无需再拆
          先穿裤子;
          再穿上衣;
          再穿袜子;
    }     
    def 小明洗漱(){   //第三层抽象, 底层细节,无需再拆
          先刷牙;
          再洗脸;
    }
    。。。
    

    原来的1个函数拆成了6+个函数,值得吗?

    值得!!!

    原因:

    1. 更清晰
    2. 更灵活

    如果小明周末去打球,优化后的函数直接复用:

    def 小明今天不上学(){
          小明睡到自然醒();
          小明穿衣();    //直接重用
          小明洗漱();    //直接重用
          小明吃早餐();  //直接重用
          小明去玩();
    }
    

    你用没拆前的函数复用试试?

    另一点:函数不要有副作用

    反例:

    def 起床没(小明){
          小明睁眼;
          小明看闹钟;
          小明穿上了裤子;
          if 时间还早:
               return false
          else:
               return true
    }
    

    这是一个有副作用的函数:

    作用一:判断小明是否起床
    作用二:偷偷给小明穿了裤子

    这么写函数的人很多!!!

    人们总想着在一个函数里做更多的事情,吃喝拉撒都在里面
    后来要改bug的时候,才发现自己写的代码变量太多,变化太多,自己都读不懂,改不动!

    只有拼图足够小的时候,它才能

    1. 想拼啥就拼啥!
    2. 想拆哪就拆哪!
    3. 想改哪就改哪!

    不知道怎么拆的时候,记住那句话:

    “每个函数一个抽象层级”


    准则三:合理注释

    注释是一种必须的恶!

    注释是一种失败! 说明你已经无法用代码表述意图

    你有用注释掩盖你对代码设计的无力,或懒惰吗?
    举两个典型的例子:
    案例1:可用代码替换的注释

    def 小明玩游戏(){
         //只有在小明父母都不在家的时候,并且满足小明考了60分,或得到同意才能玩游戏
         if((parents out)&&(count>60 || permission==true)){
                 小明开始玩游戏();
         }
    }
    

    我相信你遇到过if里面一堆条件,因为写的人自己都看不明白,在上方加了一些注释

    这些注释很糟糕:

    1. 因为if写的太糟糕,才不得不加注释
    2. 如果条件变了,变成考了90分才能玩游戏,注释也得跟着变,增加了维护成本

    处理方式:

    1. 删掉注释
    2. 用“函数替换表达式“完成重构【来自书籍《重构》】
    def 小明玩游戏(){
          if(小明满足玩游戏的条件吗() == true){
                  小明开始玩游戏();
          }
    }
    def 小明满足玩游戏的条件吗(){
          if(parents out ==false) return false;
          if (count > 60 || permission == true){
                return true;
          }
          return false; 
    }
    

    案例2:废话注释

    // name
    private String name;
    
    /**
    *default xxx
    */
    private void getUser(){
         //dosomething
    }
    

    能用函数和命名自解释的,不要添加重复的注释

    好的注释:

    1. //TODO
    2. 专有名称,不方便用英文、代码直接表述

    【TODO:这个准则写的敷衍,后面补全】


    准则四:处理好异常逻辑

    那一天,程序员终于回想起了被空指针支配的恐惧!

    我相信你一定写过类似下面的代码:

    def getUserPhoneNumber(){
          user = getUser();
          if(user != null){
              phone = user.getPhone();
              if(phone!=null){
                    return phone.getNumber();
              }
          }
          return null;
    }
    

    调这个函数的地方很绝望

    def getOtherDetail(){
        phoneNumber = getUserPhoneNumber();
        if(phoneNumber==null){
              //非正常逻辑
        }else{
              //正常逻辑
        }
        return something;
    }
    

    如果something中包含phoneNumber,再外层的函数在使用phoneNumber的时候还得做判空。。。

    你的代码里充斥着: if(XXX != null)
    或者,你经常遇到:NullPointerException

    怎么解?

    从根源处杜绝!
    user = getUser(); //保证这里面的phoneNumber不为空

    实现方式:

    1. 底层判断为null,直接抛出异常,终止后续操作
    2. 使用“空对象”模式,底层获取对象为空时,用空对象替换 【细节可搜索:空对象设计模式】,这样上层任何地方拿到的对象绝不为空

    【TODO】

    准则五:划清边界

    【TODO】

    准则六: 不要容忍破窗户

    【TODO】

    相关文章

      网友评论

          本文标题:几个编程准则

          本文链接:https://www.haomeiwen.com/subject/tkukzctx.html