美文网首页Java web
一些能够提升代码质量的编程规范

一些能够提升代码质量的编程规范

作者: kirito_song | 来源:发表于2020-02-28 17:44 被阅读0次

    算是读书笔记吧

    极客时间--设计模式之美


    命名

    命名的一个原则就是以能准确达意为目标,长短主要取决于给谁看。
    学会换位思考,假设自己不熟悉这块代码,从代码阅读者的角度去考量命名是否足够直观。

    Github

    或者有两个网站可以推荐:
    CODELF可以搜索到很多Git上的代码命名
    Free App可以搜索到很多App以及Web国际化翻译

    长度

    1. 默认的、大家都比较熟知的词,可以用缩写
    2. 作用于小的,比如临时变量用短命名
    3. 作用域比较大的,比如全局变量、类名用长的命名

    这里的短,并不是让你用a1、a2这种无法辨认的名字
    而是比如用sec 表示 second、str 表示 string、num 表示 number、doc 表示 document这样的缩写。

    利用上下文简化命名

    public class User {
      private String userName;
      private String userPassword;
      private String userAvatarUrl;
      //...
    }
    //利用上下文简化为:
    User user = new User();
    user.getName(); // 借助user对象这个上下文
    
    public void uploadUserAvatarImageToAliyun(String userAvatarImageUri);
    //利用上下文简化为:
    public void uploadUserAvatarImageToAliyun(String imageUri);
    

    命名可读

    选用的单词。最优先的是能大家看懂、实在不行最起码要能读出来

    命名可搜索

    比如数组是用array,还是list。插入是用insertXXX,还是addXXX。
    这个更多的依赖统一规约,能减少很多不必要的麻烦

    特殊前缀

    抽象类、接口、常量的前缀在项目里能够统一


    注释

    类的注释要写得尽可能全面、详细。
    让人一眼就能看明白这个类的大致作用和实现思路,而不需要进去查阅大量的代码

    public函数/接口声明

    如果他会给其他业务方使用,写上注释吧。是个好习惯

    普通函数声明

    通常我们可以通过良好的命名替代注释
    但是对于一些特殊细节,比如参数和错误的特殊描述,需要进行注释

    函数内部

    函数内部的通常也是通过良好的命名以及解释性变量让代码自解释

    但是对于内部比较复杂的函数,总结性注释可以很好的对函数进行拆分,使逻辑更清晰

    public boolean isValidPasword(String password) {
      // check if password is null or empty
      if (StringUtils.isBlank(password)) {
        return false;
      }
    
      // check if the length of password is between 4 and 64
      int length = password.length();
      if (length < 4 || length > 64) {
        return false;
      }
        
      // check if password contains only a~z,0~9,dot
      for (int i = 0; i < length; ++i) {
        char c = password.charAt(i);
        if (!((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.')) {
          return false;
        }
      }
      return true;
    }
    

    注释的维护成本

    注释本身有一定的维护成本,所以并非越多越好。撰写精炼的注释,也是一门艺术


    函数、类的大小

    大小这个东西很主观,就像对于“放盐少许”中的“少许”,即便是大厨也很难告诉你一个特别具体的量值。

    函数代码行数,不要超过一个显示屏的垂直高度

    一般来讲大概是50

    超过一屏之后,在阅读代码的时候,为了串联前后的代码逻辑,就可能需要频繁地上下滚动屏幕,阅读体验不好不说,还容易出错

    类的大小,以不影响阅读为准

    当一个类的代码读起来让你感觉头大了,实现某个功能时不知道该用哪个函数了,想用哪个函数翻半天都找不到了,只用到一个小功能要引入整个类(类中包含很多无关此功能实现的函数)的时候,这就说明类的行数过多了。

    善用空行分割单元块

    对于比较长的函数,如果

    1. 逻辑上可以分为几个独立的代码块
    2. 不方便将这些独立的代码块抽取成小函数的情况下:

    通过添加空行的方式,让这些不同模块的代码之间,界限更加明确。


    一些具体的函数编写技巧

    这些技巧,可以显著的提升同一份代码的质量

    将代码拆分成更小的单元块

    要有模块化和抽象思维,善于将大块的复杂逻辑提炼成类或者函数,屏蔽掉细节
    让阅读代码的人不至于迷失在细节中,这样能极大地提高代码的可读性,聚焦于业务

    
    // 重构前的代码
    public void invest(long userId, long financialProductId) {
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(date);
      calendar.set(Calendar.DATE, (calendar.get(Calendar.DATE) + 1));
      if (calendar.get(Calendar.DAY_OF_MONTH) == 1) {
        return;
      }
      //...
    }
    
    // 重构后的代码:提炼函数之后逻辑更加清晰
    public void invest(long userId, long financialProductId) {
      if (isLastDayOfMonth(new Date())) {
        return;
      }
      //...
    }
    
    public boolean isLastDayOfMonth(Date date) {
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(date);
      calendar.set(Calendar.DATE, (calendar.get(Calendar.DATE) + 1));
      if (calendar.get(Calendar.DAY_OF_MONTH) == 1) {
       return true;
      }
      return false;
    }
    

    避免函数参数过多

    函数包含 3、4 个参数的时候还是能接受的
    再多,就需要重构代码进行优化

    通常我们会将参数封装成对象

    
    public void postBlog(String title, String summary, String keywords, String content, String category, long authorId);
    
    // 将参数封装成对象
    public class Blog {
      private String title;
      private String summary;
      private String keywords;
      private Strint content;
      private String category;
      private long authorId;
    }
    public void postBlog(Blog blog);
    

    不过,这样在使用的时候,不同环境下的参数定义可能随着迭代就不那么明确了,有利有弊吧。

    迭代时,bool值传入方法作为隔离依据

    这个情况对于很多开发人员都很常见,尤其在修改迭代他人代码的时候。
    这种打补丁的方式,随着补丁的增多,对项目也是毁灭性的。

    void configXXXX() {
       //基本操作...
    }
    
    //迭代后
    void configXXXX(isModeA , isModeB) {
       //基本操作...
      if (isModeA) {
        //特殊操作...
      }
      //基本操作...
      if (isModeB) {
        //特殊操作
      }
    
      //基本操作...
      if (isModeA && !isModeB) {
        //特殊操作...
      } else if (isModeA){
        //特殊操作...
      }
      //基本操作...
    }
    

    函数设计要职责单一

    不要设计一个大而全的函数,而在函数的内部做一大堆的盘点。
    最起码,具体的功能实现函数不要耦合其他职责的逻辑

    比如字符串的校验时,电话、用户名、邮箱三者的实现逻辑(将来)很可能是不同的,要分开定义。

    public boolean checkUserIfExisting(String telephone, String username, String email)  { 
      if (!StringUtils.isBlank(telephone)) {
        User user = userRepo.selectUserByTelephone(telephone);
        return user != null;
      }
      
      if (!StringUtils.isBlank(username)) {
        User user = userRepo.selectUserByUsername(username);
        return user != null;
      }
      
      if (!StringUtils.isBlank(email)) {
        User user = userRepo.selectUserByEmail(email);
        return user != null;
      }
      
      return false;
    }
    
    // 拆分成三个函数
    public boolean checkUserIfExistingByTelephone(String telephone);
    public boolean checkUserIfExistingByUsername(String username);
    public boolean checkUserIfExistingByEmail(String email);
    

    移除过深的嵌套层次

    通常是if-else、switch-case、for 循环过度嵌套导致
    我们可以最开始按照执行顺去去编写多层代码,功能开发完毕函数稳定之后,再回过头来着手移除

    • 去掉多余的 else 语句
    public double caculateTotalAmount(List<Order> orders) {
      if (orders == null || orders.isEmpty()) {
        return 0.0;
      } else { // 此处的else可以去掉
        double amount = 0.0;
        for (Order order : orders) {
          if (order != null) {
            amount += (order.getCount() * order.getPrice());
          }
        }
        return amount;
      }
    }
    
    • 去掉多余的if
    public List<String> matchStrings(List<String> strList,String substr) {
      List<String> matchedStrings = new ArrayList<>();
      if (strList != null && substr != null) {
        for (String str : strList) {
          if (str != null) { // 跟下面的if语句可以合并在一起
            if (str.contains(substr)) {
              matchedStrings.add(str);
            }
          }
        }
      }
      return matchedStrings;
    }
    
    • 提前退出嵌套
    // 重构前的代码
    public List<String> matchStrings(List<String> strList,String substr) {
      List<String> matchedStrings = new ArrayList<>();
      if (strList != null && substr != null){ 
        for (String str : strList) {
          if (str != null && str.contains(substr)) {
            matchedStrings.add(str);
            // 此处还有10行代码...
          }
        }
      }
      return matchedStrings;
    }
    
    // 重构后的代码:使用return和continue提前退出
    public List<String> matchStrings(List<String> strList,String substr) {
      List<String> matchedStrings = new ArrayList<>();
      if (strList == null || substr == null){ 
        return matchedStrings;
      }
      for (String str : strList) {
        if (str == null || !str.contains(substr)) {
          continue; 
        }
        matchedStrings.add(str);
        // 此处还有10行代码...
      }
      return matchedStrings;
    }
    
    • 将部分嵌套逻辑封装成函数调用

    对于无法通过逻辑顺序进行优化的代码,可以通过把局部功能封装成小的函数来减轻主函数的嵌套层级。

    
    // 重构前的代码
    public List<String> appendSalts(List<String> passwords) {
      if (passwords == null || passwords.isEmpty()) {
        return Collections.emptyList();
      }
      
      List<String> passwordsWithSalt = new ArrayList<>();
      for (String password : passwords) {
        if (password == null) {
          continue;
        }
        if (password.length() < 8) {
          // ...
        } else {
          // ...
        }
      }
      return passwordsWithSalt;
    }
    
    // 重构后的代码:将部分逻辑抽成函数
    public List<String> appendSalts(List<String> passwords) {
      if (passwords == null || passwords.isEmpty()) {
        return Collections.emptyList();
      }
    
      List<String> passwordsWithSalt = new ArrayList<>();
      for (String password : passwords) {
        if (password == null) {
          continue;
        }
        passwordsWithSalt.add(appendSalt(password));
      }
      return passwordsWithSalt;
    }
    
    private String appendSalt(String password) {
      String passwordWithSalt = password;
      if (password.length() < 8) {
        // ...
      } else {
        // ...
      }
      return passwordWithSalt;
    }
    

    解释性变量

    • 常量取代魔法数字
    
    public double CalculateCircularArea(double radius) {
      return (3.1415) * radius * radius;
    }
    
    // 常量替代魔法数字
    public static final Double PI = 3.1415;
    public double CalculateCircularArea(double radius) {
      return PI * radius * radius;
    }
    
    • 解释性变量来解释复杂表达式
    if (date.after(SUMMER_START) && date.before(SUMMER_END)) {
      // ...
    } else {
      // ...
    }
    
    // 引入解释性变量后逻辑更加清晰
    boolean isSummer = date.after(SUMMER_START)&&date.before(SUMMER_END);
    if (isSummer) {
      // ...
    } else {
      // ...
    } 
    
    • 多个复合条件的bool,最好加上注释
    // ((团购 &&  非折扣 && 有销售价格) || 有团购列表)
    boolean showGroupBuyButton = ((isGroupbuy && !isSale && (price>0)) || groupBuyList.count)
    

    相关文章

      网友评论

        本文标题:一些能够提升代码质量的编程规范

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