设计模式学习笔记 - 规范与重构 - 6.快速改善代码质量的20条编程规范

前言

在这里插入图片描述

编码规范大部分都简单明了,在代码细节方面,能立竿见影地改善质量。

前面《规范与重构 - 1.什么情况下要重构?重构什么?又该如何重构?》讲过,持续低层次、小规模重构依赖的基本上都是编码规范,这也是改善代码可读性的有效手段。

关于编码规范有很多书籍讲的比较好。这里,是根据个人的开发经验,罗列了 20 条最好用的编码规范,一共分为三个部分:命名与注释、代码风格和编程技巧。


命名与注释

命名

大到项目名、模块名、对外暴露的接口,小到类名、函数名、变量名、参数名,只要是做开发,我们就逃不过 “起名字” 这一关。命名的好坏,对于代码的可读性来说非常重要。此外,命名能力也体现一个程序员的基本素养。

取一个特别合适(能达意)的名字是一件非常有挑战的事情。实际上,命名这件事说难也不难,关键还是看你是否重视。对于影响范围较大的命名,比如包名、接口、类名,我们一定要反复斟酌、推敲

如果实在想不到好名字,可以去 GitHub 上用相关的关键词联想搜索下,看看类似的代码是怎么命名的。

具体如何命名,就从下面的 4 点讲解。

1.命名多长合适?

  • 有些人喜欢使用很长的命名方式,觉得命名一定要准确达意,哪怕长一点也没关系,所以,这类人写的代码,类名、函数名都很长。
  • 另外一些人喜欢用短的命名方式,能用缩写尽量用缩写,所以,代码中都是各种缩写的命名。

尽管长命名可以包含更多的信息,更能准备直观地表达意图,但是,如果函数、变量的名字很长,那由它们组成的语句就会很长。在代码列长度有限的情况下,就会经常出现一条语句被分割成两行的情况,这会影响代码可读性。

实际上,在足够表达含义的情况下,命名当然是越短越好。但是,大部分情况下,短的命名没有长的命名更能达意。对于一些默认的、大家都比较熟知的词,比较推荐用缩写。这样可以让命名短一些,又不影响阅读理解,比如 sec 表示 second,str 表示 String、num 表示 number、doc 表示 document。

另外,对于作用域比较小的变量,我们可以使用相对短的命名,比如一些函数内的局部变量。相反,对于类名这种作用域比较大的,更推荐使用长命名方式

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

对于代码的编写者来说,自己对代码的逻辑很清楚,总感觉用什么样的命名都可以达意,实际上,对于不熟悉你代码的同事来讲,可能就不这么认为了。所以,命名的时候,可以换位思考下,假设自己不熟悉这块代码,从代码阅读者角度去考量命名是否足够直观

2.利用上下文简化命名

public class User {
    private String userName;
    private String userPassword;
    private String userAvatarUrl;
    //...
}

User 类这样一个上下文中,我们没有必要在成员变量命名中重复 “user” 这样的前缀,而是应该直接接命名为 name、password、avatarUrl。在使用这些属性的时候,我们能借助对象这样一个上下文,表达意思也足够明确。具体代码如下所示:

User user = new User();
user.getName(); // 借助user对象这个上下文

除了类之外,函数参数也可以借助函数这个上下文来简化命名。

public void uploadUserAvatarImageToAliyun(String userAvatarImageUri);
// 利用上下文简化为
public void uploadUserAvatarImageToAliyun(String imageUri);

3.命名要可读、可搜索

先解释下,这里的 “可读”,指的是不要用一些特别生僻、难发音的英文单词来命名。

特别生僻、难发音的命名,在沟通的时候,每当提起的名字时,都会尴尬地卡顿一下。虽然我们并不排斥一些独特的命名方式,但起码得让大部分人看一眼就知道怎么读

再解释下命名的可搜索。我们在 IDE 中编写代码时,经常会用 “关键词联想” 的方法来自动补全和搜索。

  • 比如,键入某个对象 “.get”,希望 IDE 返回这个对象的所有 get 开始的方法。
  • 再比如,键入 “Array”,搜索 JDK 中数组相关的类。

我们在命名时,最好能符合整个项目的命名习惯。

  • 大家都用 “selectXXX” 表示查询,你就不要用 “queryXXX” ;
  • 大家都用 “insertXXX” 表示插入,你就不要用 “addXXX” ;

统一规约很重要,能减少很多不必要的麻烦

4.命名接口和抽象类

对于接口的命名,一般有两种比较常见的方式。

  • 一种是加前缀 “I”,表示一个 interface。比如 IUserService 对应的实现类命名为 UserService
  • 另一种是不加前缀,比如 UserService 对应的实现类命名为 UserServiceImpl

对于抽象类的命名,也有两种方式。

  • 一种是带上前缀 “Abstract”,比如 AbstractConfiguration
  • 另一种是不带 “Abstract” 前缀。

对接接口和抽象类,选择哪种命名方式都是可以的,只要项目里能够统一就行

注释

注释和命名同样重要。

很多人认为,好的命名完全可以替代注释。如果需要注释,那说明命名不够好,需要在命名上下功夫,而不是添加注释。

实际上,我个人觉得,这样的观点太极端。命名再好,毕竟有长度限制,而这个时候,注释就是一个很好的补充。

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 {
}

有些人认为,注释是要提供一些代码没有的额外信息,所以不要写 “做什么、怎么做”,这两方面在代码中都可以体现出来,只要写清楚 “为什么”,表明代码的设计意图即可。 我个人不是特别认可这样的观点。理由有以下 3 点。

1.注释比代码承载的信息更多。

命名的主要目的是解释 “做什么”,比如, void increaseWalletAvailableBalance(BigDecimal amount) 表明这个函数用来增加钱包的可用余额, boolean isValidatePassword 表明这个变量用来表示密码是否合法。函数和变量如何命名得好,确实可以不用在注释中解释它是做什么的。但是对于类来说,包含的信息比较多,一个简单的命名就不够全面详尽了。这个时候,在注释中写明它是 “做什么” 就合情合理了。

2.注释起到总结性作用、文档的作用。

代码之下无私密。阅读代码可以明确地知道代码是 “怎么做” 的。那注释中就不用写 “怎么做” 了?

实际上也可以写。在注释中,关于具体的代码实现思路,我们可以写一些总结性的说明、特殊情况说明。这样能够让阅读代码的人通过注释就大概了解代码的实现思路,阅读起来就会更加容易。

实际上,对于一些比较复杂的类或者接口,我们可能还需要在注释写清楚 “如何用”,举一些简单的 “quick start” 的例子,让使用者在不阅读代码的情况下,快速地知道该如何使用。

3.一些总结性注释能让代码的结构更加清晰。

对于逻辑比较复杂的代码或者比较长的函数,如果不好提炼、不好拆分成小的函数,那我们可以借助总结性注释让代码结构更清晰、更有条理。

public boolean isValidatePassword(String password) {
    // check if password is null or empty
    if (password == null || password.isEmpty()) {
        return false;
    }
    
    // check if the length of the 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 ch = password.charAt(i);
        if (!((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '.')){
            return false;
        }
    }
    return true;
}

2.注释是不是越多越好

注释太多和太少都有问题。

  • 太多,可能意味着代码写得不够可读,主要写很多注释来补充。此外,注释太多也会对代码本身的阅读起到干扰的作用。而且后期的维护成本也比较高,有时候代码改了,注释忘了同步修改,就会让代码阅读者更加迷惑。
  • 当然,如果代码中一行注释都没有,那只能说明这个程序员很懒,我们要适当督促一下,让它注意添加一些必要的注释。

按照本人的经验来说,类和函数一定要写注释,而且要写的尽可能全面、详细,而函数内部的注释要相对少一些,一般都是靠好的命名、解释性变量、总结性注释来提高代码的可读性

代码风格

说起代码风格,其实很难说哪种风格更好。最重要的,也是最需要我们要做到的,是在团队、项目中保持风格统一,让代码像同一个人写出来的,整齐划一。这样能减少阅读干扰,提高代码的可读性。这才是我们在实际工作中想要实现的目标。

类、函数多大才合适?

类或函数的代码行数不能太多,但也不能太少。类或函数的代码行数太多,一个类上千行,一个函数几百行,逻辑过于繁杂,阅读代码的时候,很容易就会看了后面忘了前面。相反,类或函数的代码行数太少,在代码总量相同的情况下,被分割成的类和函数会相应增多,调用关系就会变得复杂,阅读代码的时候,需要在 n 个类或 n 个函数之间跳来跳去,阅读体验也不好。

对于函数代码行数的最大限制,网上有一种方法,就是不要超过一个相识屏的垂直高度。比如,在我的电脑上,如果要让一个函数完整地显示在 IDE 中,那最大代码行数不能超过 50。

这个说法挺有道理的。超过一屏之后,在阅读代码的是,为了串联前后的代码逻辑,就可能需要频繁地上下滚动屏幕,阅读体验不好。

对类的代码行数的最大限制,这个就更难给出了。 当一个类的代码读起来让你感觉头大了,实现某个功能时不知道该用哪个函数了,想用哪个函数半天找不到了,只用到一个小功能要引入整个类的时候,这就说明类的行数过多了。

一行代码多长最合适?

总体上来说,我们要遵循一个原则:一行代码长度不能超过 IDE 显示的宽度。需要滚动鼠标才能查看一行全部代码,显然不利于阅读代码。当然,这个限制也不能太小,太小的话会导致稍长一点的语句被折成两行,也会影响到代码的整洁,不利于阅读。

善用空行分割单元块

对于较长的函数,如果逻辑上可以分为几个独立的代码块,在不方便将这些独立的代码块抽取成小函数的情况下,为了让逻辑更加清晰,除了上一节课中提到的用总结性注释的方法外,我们还可以使用空行来分割各个代码块。

此外,在类的成员变量与函数之间、静态成员变量与普通成员变量之间、各函数之间、甚至各成员变量之间,我们都可以通过添加空行的方式,让这些不同模块的代码之间,界限更加明确。写代码就类似写文章,善于应用空行,可以让代码的整体结构看起来更加油清晰、有条理。

四格缩进还是两格缩进?

我觉的这个取决于个人喜好,在项目内部能够统一就行。

当然,还有一个选择的标准,就是跟业内推荐的风格统一、跟著名的开源项目统一。这样,当我们拷贝一些开源的代码到项目里时,能够让引入的代码跟我们项目本身的代码,保持风格统一。

此外,值得强调的是,不管是用两格缩进还是四格缩进,一定不要用 tab 键缩进。因为在不同的 IDE 下,tab 键的显示宽度不同,有的显示为四格缩进,有的显示为两格缩进。

大括号是否要另起一行?

// php
class ClassName
{
	public function foo()
	{
		// method body
	}
}

// java
public class ClassName {
	public void foo() {
		// method body
	}
}

个人比较推荐,将括号和语句同一行的风格。当然,还是那句话,只要跟团队统一、业内统一、跟开源项目看齐就好了,没有绝对的优劣之分。

类中的成员的排列顺序

在 Java 类文件汇总,先要书写类所属的包名,然后罗列 import 引入的依赖类。依赖类按照字母顺序从小大道排列。

在类中,成员变量排在函数的前面。成员变量之间或函数之间,都是按照 “先静态(静态函数或静态成员变量)、后普通(非静态函数或非静态成员变量)” 的方式来排列。此外,成员变量之间或函数之间,还会按照作用域范围从大到小的顺序来排列,先写 public 成员变量或函数,然后是 protected,最后是 private 的。

编程技巧

这部分技巧比较琐碎,很难罗列全面,我仅仅总结了一些我认为比较关键的,更多的技巧需要你在实践中自己慢慢总结、积累。

把代码分割成更小的单元块

大部分人的阅读习惯都是,先看整体在看细节。所以,我们要有模块化和抽象思维,善于将大块的复杂逻辑提炼成函数或者类,屏蔽掉细节,让阅读代码的人不至于迷失在细节中,这样能极大地提高代码的可读性。不过,只有代码逻辑比较复杂的时候,我们其实才建议提炼类或者函数。如果提炼出的函数只包含两三行代码,在阅读代码的时候,还得跳过去看一下,这样反倒增加了阅读成本。

举个例子来解释一下。代码具体如下所示。重构前,在 invest() 函数中,最开始那段关于时间处理的代码,是不是很难看懂?重构之后,将这部分逻辑抽象成一个函数,并且命名为 isLastDayOfMonth,从名字上就能清晰地了解它的功能,判断今天是不是当月的最后一天。这里,我们就是通过将复杂的逻辑代码提炼成函数,大大地提高了代码的可读性。

// 重构前的代码
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(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 个参数的时候还是能接受的,大于等于 5 个的时候,参数就有点多了,会影响到代码的可读性,使用起来也不方便。针对参数过多的情况,一般有两种处理方法。

1.考虑函数是否职责单一,是否能通过拆分成多个函数的方式来减少参数。

public User getUser(String username, String telephone, String email);

// 拆分成多个函数
public User getUserByUsername(String username);
public User getUserByTelephone(String telephone);
public User getUserByEmail(String email);

2.将函数的参数封装成对象。

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);

另外,如果函数是对外暴露的远程接口,将参数封装成对象,还可以提高接口的兼容性。在王接口中添加新的参数时,老的远程接口调用者有可能就不需要修改代码来兼容的新接口了。

勿用函数参数来控制逻辑

不要在函数中使用布尔类型的表示参数来控制内部逻辑, 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);

不过,如果函数是 private 私有的,影响范围有限,或者拆分之后的两个函数经常同时被调用,可以酌情考虑保留标识参数。

// 拆成两个函数的调用方式
boolean isVip = false;
if (isVip) {
    buyCourse(userId, courseId);
} else {
    buyCourseForVip(userId, courseId);
}

// 保留标识参数的调用方式更加简洁
boolean isVip = false;
// ...
buyCourse(userId, courseId, isVip);

除了布尔类型作为标识参数来控制逻辑的情况外,还有一种 “根据参数是否为 null” 来控制逻辑的情况。针对这种情况,我们也应将其拆分成多个函数。拆分之后的函数职责更加明确,不容易用错。具体代码如下所示:

public List<Transaction> selectTransactions(Long userId, Date startDate, Date endDate) {
    if (startDate != null && endDate != null) {
        // 查询两个时间区间的 transactions
    }
    if (startDate != null && endDate == null) {
        // 查询startDate之后的所有 transactions
    }
    if (startDate == null && endDate != null) {
        // 查询endDate之前的所有 transactions
    }
    if (startDate == null && endDate == null) {
        // 查询所有的 transactions
    }
}

// 拆分成多个public函数,更加清晰易用
public List<Transaction> selectTransactionsBetween(Long userId, Date startDate, Date endDate) {
    return selectTransactions(userId, startDate, endDate);
}

public List<Transaction> selectTransactionsStartWith(Long userId, Date startDate) {
    return selectTransactions(userId, startDate, null);
}

public List<Transaction> selectTransactionsEndWith(Long userId, Date endDate) {
    return selectTransactions(userId, null, endDate);
}

public List<Transaction> selectAllTransactions(Long userId) {
    return selectTransactions(userId, null, null);
}

private List<Transaction> selectTransactions(Long userId, Date startDate, Date endDate) {
    //...
}

函数设计要职责单一

前面讲单一职责原则的时候,针对的是类、模块这样的应用对象。实际上,对于函数的设计来说,更要满足单一职责原则。相对于类和模块,函数的粒度比较小,代码行数少,所以在应用单一职责原则的时候,没有像应用到类或者模块那样模棱两可,能多单一就多单一。

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 循环过度嵌套导致的。个人建议,嵌套最好不要超过两层,超过两层之后就要思考一下是否可以减少嵌套。过深的嵌套本身理解起来就比较费劲。此外,嵌套过深很容易因为代码多次缩进,导致嵌套内部的语句超过一行的长度而折成两行,影响代码的整洁。

解决嵌套过深的方法也比较成熟,有下面 4 中常见的思路。

1.去掉多余的 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> matchString(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;
}

2.使用编程语言提供的 continue、break、return 关键字,提前退出嵌套

// 重构前的代码
public List<String> matchString(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;
}

// 重构后的代码:使用continue提前退出
public List<String> matchString(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);
        }
    }
    return matchedStrings;
}

3.调整顺序来减少嵌套

// 重构前的代码
public List<String> matchString(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> matchString(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;
}

4.将部分嵌套逻辑封装成函数调用,以此来减少嵌套

// 重构前代码
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() == 0) {
            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;
}

此外,常用的还有通过使用多态来替代 if-else、switch-case 条件判断的方法。这个思路设计代码结构的改动,我们在后面的再讲。

学会使用解释性变量

常用的解释性变量来提高代码的可读性的情况有下面 2 种。

1.常量取代魔法数字

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;
}

2.使用解释下变量来解释复杂表达式

if (date.after(SUMMER_START) && date.before(SUMMER_END)) {
    // ...
} else {
    // ...
}

// 引入解释下变量后逻辑更加清晰
boolean isSummer = date.after(SUMMER_START) && date.before(SUMMER_END);
if (isSummer) {
    // ...
} else {
    // ...
}

总结

1.关于命名

  • 命名的关键是能准确达意。对于不同作用域的命名,我们可以适当地选择不同的长度。
  • 可以借助类的信息来简化属性、函数的命名,利用函数的信息来简化函数参数的命名。
  • 命名要可读、可搜搜。不要使用生僻的、不好读的英文单词来命名。命名要符合项目的统一规范,也不要用些反直觉的命名。
  • 接口由两种命名方式:一种是在接口总带前缀 “I”;另一种是在接口的实现类中带后缀 “Impl”。对于抽象类,也有两种方式,一种是带上前缀 “Abstract”,一种是不带前缀。这两种都可以,关键是要在项目中统一。

2.关于注释

  • 注释的内容主要包含这样三个方面:做什么、为什么、怎么做。对于一些复杂的类和接口,我们可能还需要写明 “如何用”。
  • 类和函数的注释一定要写注释,而且要写的尽可能全面详细。函数内部的注释要相对少一些,一般都是靠好的命名、提炼函数、解释性变量、总结性注释来提高代码的可读性。

3.关于代码风格

  • 函数、类多大才合适?函数的代码行数不要超过一屏幕的大小,比如 50 行。类的大小限制比较难确定。
  • 一行代码多长合适?最好不要超过 IDE 的显示宽度。当然,也不能太小,否则会导致很多稍微长点的语句被折成两行,也会影响到代码的整洁,不易于阅读。
  • 四格缩进还是两格缩进?个人比较推荐四格缩进。不管是用四个缩进还是两格缩进,一定不要用 tab 键。
  • 大括号是否要另起一行?将大括号放到跟上一条语句同一行,可以节省代码行数。但是将大括号另起新的一行的方式,左右括号可以垂直对齐。
  • 类中成员怎么排列? 依赖类按照字母序从小到大排列。类中先写变量后写函数。成员变量或函数之间,先写静态成员变量或函数,后写普通变量或函数,且按照作用域大小依次排列。

4.关于编码技巧

  • 将复杂逻辑提炼拆分成函数和类。
  • 通过拆分多个函数或将参数封装为对象的方式,来处理参数过多的情况。
  • 函数中不要使用参数来做代码执行逻辑的控制。
  • 函数设计要职责单一。
  • 移除过深的嵌套层次,方法包括:去掉多余的 if 或 else 语句,使用 continue、break、return 关键字提前退出嵌套,调整执行顺序来减少嵌套,将部分嵌套逻辑抽象成函数。
  • 用字面常量取代魔法值
  • 用解释性变量来解释复杂表达式,以此提高代码可读性。

5.统一编码规范

对了比较细节的知识点外,最后,还有一条非常重要的点,那就是,项目、团队,甚至公司,一定要制定统一的编码规范,并且通过 Code Review 督促执行,这对提高代码质量有立杆见影的效果。

  • 19
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值