设计模式简记-快速改善代码质量的编程规范

4.2 快速改善代码质量的编程规范

4.2.1 命名

命名的一个原则就是以能准确达意为目标

4.2.1.1 多长合适
  • 作用域大的用更达意的长命名,如类名;反之用短命名
4.2.1.2 利用上下文

如下代码:不应在成员变量的命名中重复添加“user”这样一个前缀单词,而应直接命名为 name、password、avatarUrl

public class User {
  private String userName;
  private String userPassword;
  private String userAvatarUrl;
  //...
}
4.2.1.3 命名要可读、可搜索
  • 可读,指的是不要用一些特别生僻、难发音的英文单词来命名。如:plateaux, eyrie

  • 可搜索:经常会用“关键词联想”的方法来自动补全和搜索。比如,键入某个对象“.get”,希望 IDE 返回这个对象的所有 get 开头的方法。

    统一规约很重要,都用“selectXXX”表示查询,就不要用“queryXXX”;都用“insertXXX”表示插入一条数据,要不用“addXXX”

4.2.1.4 如何命名接口和抽象类
  • 接口命名:两种比较常见的方式:
    1. 加前缀“I”,表示一个 Interface。比如 IUserService;
    2. 不加前缀,实现类加Impl。如UserService,实现类UserServiceImpl
  • 抽象类命名:加前缀“Abstract”,如AbstractUserService

4.2.2 注释

4.2.2.1 该写什么?
  • 做什么、为什么、怎么做
/**
* (what) Bean factory to create beans. 
* 
* (why) The class likes Spring IOC framework, but is more lightweight. 
*
* (how) Create objects from different sources sequentially:
* user specified object > SPI > configuration > default object.
*/
public class BeansFactory {
  // ...
}
4.2.2.2 注释要多吗?
  • 类和函数一定要写注释,而且要写得尽可能全面、详细;
  • 函数内部的注释要相对少一些,一般都是靠好的命名、提炼函数、解释性变量、总结性注释来提高代码的可读性。

4.2.3 代码风格

    1. 成员变量排在函数的前面;
    2. 成员变量之间或函数之间,按照“先静态、后普通”的方式来排列;
    3. 成员变量之间或函数之间,还会按照作用域范围从大到小的顺序来排列,先 public ,再 protected ,最后 private 。

4.2.4 实用编程技巧

4.2.4.1 代码分隔成更小的代码块
  • 阅读代码的习惯都是,先看整体再看细节。所以,要有模块化和抽象思维,善于将大块的复杂逻辑提炼成类或者函数,屏蔽掉细节,让阅读代码的人不至于迷失在细节中,这样能极大地提高代码的可读性。

    // 重构前的代码
    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;
    }
    
4.2.4.2 避免函数参数过多
  • 考虑函数是否职责单一,是否能通过拆分成多个函数的方式来减少参数

    public User getUser(String username, String telephone, String email);
    
    // 拆分成多个函数
    public User getUserByUsername(String username);
    public User getUserByTelephone(String telephone);
    public User getUserByEmail(String email);
    
  • 将函数的参数封装成对象

    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);
    
4.2.4.3 勿用函数参数来控制逻辑
  • true 的时候走这块逻辑,false 的时候走另一块逻辑。违背了单一职责原则和接口隔离原则。将其拆成两个函数,可读性上也要更好

    public void buyCourse(long userId, long courseId, boolean isVip);
    
    // 将其拆分成两个函数
    public void buyCourse(long userId, long courseId);
    public void buyCourseForVip(long userId, long courseId);
    
4.2.4.4 函数设计要职责单一
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);
4.2.4.5 移除过深的嵌套层次
  • 去掉多余的 if 或 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;
      }
    }
    
    // 示例二
    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;
    }
    
  • 使用编程语言提供的 continue、break、return 关键字,提前退出嵌套

    // 重构前的代码
    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;
    }
    
    // 重构后的代码:使用continue提前退出
    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)) {
            continue; 
          }
          matchedStrings.add(str);
          // 此处还有10行代码...
        }
      }
      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) {
            if (str.contains(substr)) {
              matchedStrings.add(str);
            }
          }
        }
      }
      return matchedStrings;
    }
    
    // 重构后的代码:先执行判空逻辑,再执行正常逻辑
    public List<String> matchStrings(List<String> strList,String substr) {
      if (strList == null || substr == null) { //先判空
        return Collections.emptyList();
      }
    
      List<String> matchedStrings = new ArrayList<>();
      for (String str : strList) {
        if (str != null) {
          if (str.contains(substr)) {
            matchedStrings.add(str);
          }
        }
      }
      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;
    }
    
4.2.4.6 使用解释性变量
  • 常量取代魔法数字

    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 {
      // ...
    } 
    
4.2.4.7 善用编程语言提供的语法糖,如lambda表达式

https://www.cnblogs.com/franson-2016/p/5593080.html

list循环操作

String[] atp = {"Rafael Nadal", "Novak Djokovic",  
       "Stanislas Wawrinka",  
       "David Ferrer","Roger Federer",  
       "Andy Murray","Tomas Berdych",  
       "Juan Martin Del Potro"};  
List<String> players =  Arrays.asList(atp);  
  
// 以前的循环方式  
for (String player : players) {  
     System.out.print(player + "; ");  
}  
  
// 使用 lambda 表达式以及函数操作(functional operation)  
players.forEach((player) -> System.out.print(player + "; "));  
   
// 在 Java 8 中使用双冒号操作符(double colon operator)  
players.forEach(System.out::println);

实现Runnable接口

// 1.1使用匿名内部类  
new Thread(new Runnable() {  
    @Override  
    public void run() {  
        System.out.println("Hello world !");  
    }  
}).start();  
  
// 1.2使用 lambda expression  
new Thread(() -> System.out.println("Hello world !")).start();  
  
// 2.1使用匿名内部类  
Runnable race1 = new Runnable() {  
    @Override  
    public void run() {  
        System.out.println("Hello world !");  
    }  
};  
  
// 2.2使用 lambda expression  
Runnable race2 = () -> System.out.println("Hello world !");  
   
// 直接调用 run 方法(没开新线程哦!)  
race1.run();  
race2.run();

使用Lambdas排序集合

// 1.1 使用匿名内部类根据 name 排序 players  
Arrays.sort(players, new Comparator<String>() {  
    @Override  
    public int compare(String s1, String s2) {  
        return (s1.compareTo(s2));  
    }  
});
// 1.2 使用 lambda expression 排序 players  
Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));  
Arrays.sort(players, sortByName);  
  
// 1.3 也可以采用如下形式:  
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));  
posted @ 2020-06-06 18:37  杨海星  阅读(197)  评论(0编辑  收藏  举报