编写高质量代码的技巧、方法和理论

讲在前面

代码质量的评价有很强的主观性。对于同样一个系统,熟悉它的资深工程师会觉得代码的可维护性还不错,而一些新人因为不熟悉代码,修改 bug、修改添加代码要花费很长的时间,就有可能会觉得代码的可维护性不那么好。

可维护性

落实到编码开发,所谓的“维护”无外乎就是修改 bug、修改老的代码、添加新的代码之类的工作。

所谓“代码易维护”就是指,在不破坏原有代码设计、不引入新的 bug 的情况下,能够快速地修改或者添加代码。所谓“代码不易维护”就是指,修改或者添加代码需要冒着极大的引入新 bug 的风险,并且需要花费很长的时间才能完成。

对于一个开发者来说,什么样的代码是“可维护”的呢?应该是能看懂、易修改、可测试的代码。对应的,也就是可读性、可扩展性&可复用性、可测试性。

可读性

代码的可读性应该是评价代码质量最重要的指标。

  1. 命名规范:命名技巧动作➕名词,如 AssertMemberCommentPrivilege。
  2. 参数个数:参数个数控制在 3、4 个以内,如果太多的话应该传入一个“结构体”。
  3. 函数大小:一般在 100 行以内。

如何检验可读性:

实际上,code review 是一个很好的测验代码可读性的手段。如果你的同事可以轻松地读懂你写的代码,那说明你的代码可读性很好;如果同事在读你的代码时,有很多疑问,那就说明你的代码可读性有待提高了。

可扩展性

说直白点就是,代码预留了一些功能扩展点,你可以把新功能代码,直接插到扩展点上,而不需要因为要添加一个功能而大动干戈,改动大量的原始代码。

可复用性

如何实现可复用性:

  1. 利用继承、组合、多态等面向对象特性。
  2. 函数职责单一,。

可测试性

代码可测试性的好坏,能从侧面上非常准确地反应代码质量的好坏。代码的可测试性差,比较难写单元测试,那基本上就能说明代码设计得有问题。

设计原则

单一职责原则

开闭原则

对扩展开放,对修改关闭。

编码时最头疼的就是更改旧代码,因为要确保依赖旧代码的其他模块正常工作。

这条原则的设计初衷:只要它没有破坏原有的代码的正常运行,没有破坏原有的单元测试,我们就可以说,这是一个合格的代码改动。

看一个违反原则的例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class Alert {
  // ...省略AlertRule/Notification属性和构造函数...
  
  // 改动一:添加参数timeoutCount
  public void check(String api, long requestCount, long errorCount, long timeoutCount, long durationOfSeconds) {
    long tps = requestCount / durationOfSeconds;
    if (tps > rule.getMatchedRule(api).getMaxTps()) {
      notification.notify(NotificationEmergencyLevel.URGENCY, "...");
    }
    if (errorCount > rule.getMatchedRule(api).getMaxErrorCount()) {
      notification.notify(NotificationEmergencyLevel.SEVERE, "...");
    }
    // 改动二:添加接口超时处理逻辑
    long timeoutTps = timeoutCount / durationOfSeconds;
    if (timeoutTps > rule.getMatchedRule(api).getMaxTimeoutTps()) {
      notification.notify(NotificationEmergencyLevel.URGENCY, "...");
    }
  }
}

里氏替换原则

接口隔离原则

接口隔离原则:我只要我想要的,不想要的别给我

其实类似于单一职责原则,个人感觉不太重要。

有时候为了符合这一原则把接口拆分得太细也不好。

依赖反转原则

接口隔离原则:高层模块不依赖低层模块,高层模块提供一个插件&钩子来使用低层模块。我们规定好高层流程框架,在这个流程中会开放出一些接口,我们只需要把接口的实现类(低层)挂载到这个流程框架中即可实现完整的业务逻辑,而此时高层框架是完全不依赖低层的。

比如,Tomcat是一个HTTP服务器,其内部规定好了如何处理HTTP请求的报文,它给我们开放出来的规范就是一些Servlet类中的API,比如把HTTP报文转换成ServletRequest,把响应转换为ServletResponse。我们只需要编写自己的Servlet然后把这些Servlet注册到容器中,对应的HTTP请求就可以被派发到我们的Servlet对象中了。

设计原则实战

控制反转&依赖注入框架

控制反转

这里所说的“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。

依赖注入

依赖注入是一种具体的编码技巧。我们不通过 new 的方式在类内部创建依赖的类对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。

依赖注入框架

依赖注入框架:由框架来自动创建对象、管理对象的生命周期、依赖注入等原本需要程序员来做的事情。

设计模式

大部分设计模式要解决的都是代码的可扩展性问题。

如何重构

全文总结

对于提升代码质量,最有效的是编码规范,其次是设计原则,再次是代码重构,最后才是面向对象和设计模式。 整个排序是按照我理解的难易程度来进行的。一般一个项目都是多人开发,多人并行开发中,考虑到团队中技术水平不一,保持代码质量最好的方式就是先制定编码规范,大家最容易达成一致并遵守。其次是设计原则,因为它相对来讲更明确,违反设计原则基本等于硬伤,更容易得到认同。代码重构是需要长期去做的,甚至不是在开发过程中而是在维护过程中去做的,所以排在第三位。后面两项更多的要考虑实际情况,不同水平的人甚至相同水平的人都会有自己不同的理解,比较难以达成统一,项目排期足够还好,一旦排期不足,很可能在当时没那个成本去谈论这些。


原文链接:编写高质量代码的技巧、方法和理论

Buy me a coffee~
室长 支付宝支付宝
室长 微信微信
0%