几个编程准则

2020-01-18  本文已影响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】

上一篇下一篇

猜你喜欢

热点阅读