美化java代码,从合理注释开始
作者:连理枝_ 发布时间:2022-01-18 16:32:47
请停止代码注释
“干净的代码应该像写好的散文一样” - Robert C. Martin
不良代码的通病就是有很多注释。这是凌乱的源代码最明显的迹象。
每个程序员的目标应该是编写干净和富有表现力的代码,以避免代码注释。每个变量,函数和类的目的应该隐含在其名称和结构中。
当其他人读取您的代码时,他们不应该阅读注释以了解你的代码正在做什么。命名良好的类和函数应该引导读者通过你的代码,就像一本写得很好的小说一样。当读者看到一个新的类或功能时,他们不应该对他们在里面看到的东西感到困惑难以理解。
请记住,开发人员的工作时间很少花在编写代码上,花在阅读代码和理解代码上的时间要多得多。
注释掩饰错误
在代码中命名是非常重要的。您应该花费大量精力准确而精确地命名每一段代码,以便其他开发人员能够理解您的代码。
// 按状态查找员工
List<Employee> find(Status status) {
...
}
在此示例中,名称find不够描述,因此此函数的作者需要留下描述函数功能的描述性注释。当我们看到从另一个模块调用的find函数时,它的作用是一个谜。它发现了什么?究竟是什么意思?它返回了它发现的东西吗?怎么找到它发现的东西?就像鲍勃叔叔在他的书《Clean Code》中所说,如果你需要写注释,你就无法通过代码表达自己真实的用意。
我们不希望检查每个函数上面的注释,以了解它的作用。
List<Employee> getEmployeesByStatus(Status status) {
...
}
现在很明显能看出来这个函数的具体作用,这使得注释变得多余。这让我想到了注释糟糕的下一个方式。
冗余注释
这些混乱了你的代码,完全没必要。
//此函数发送电子邮件
void sendEmail() {
...
}
//此函数发送电子邮件
public class Employee {
...
}
/ **
* @param title CD的标题
* @param作者CD的作者
* @param track CD上的曲目数
* /
public void addCd(String title, String author, int tracks) {
...
}
多数情况是强制冗余。很多公司在每个功能和类别上都要求这一点。如果你的上司要求这样做,请他们不要。
错误的抽象程度
如果您有一个很长的功能或需要记录代码的哪一部分做了什么,那么您可能违反了这些规则:
1.功能应该做一件事。
2.功能应该很小。
这是一个例子
//此函数计算价格,与销售额进行比较
//促销,检查价格是否有效,然后
//向用户发送促销电子邮件
public void doSomeThings(){
//计算价格
...
...
...
//将计算出的价格与促销活动进
...
...
...
//检查计算的价格是否有效
...
...
...
//向用户发送促销信息
...
...
...
}
当你成功地将逻辑的每个部分封装到一个单独的函数中时,代码不需要注释就会表现的应该像它的作用描述一样。
重构如下:
public void sendPromotionEmailToUsers(){
calculatePrices();
compareCalculatedPricesWithSalesPromotions();
checkIfCalculatedPricesAreValid();
sendPromotionEmail();
}
而不是注释代码的每个部分,每个逻辑块应该很好地封装在它自己的函数中。
首先,这提高了可读性。每个代码块不必逐行读取。我们可以简单地读取辅助函数名称并理解它的作用。如果我们想要了解每个函数内部的更多细节,就能去看具体实现。
其次,它提高了可测试性。在上面的示例中,我们可以为每个函数单独进行单元测试。如果不封装这些单独的函数,则很难测试较大函数sendPromotionEmailToUsers()的每个部分。执行多个功能的功能很难测试。
最后,它提高了可重构性。通过将逻辑的每个部分封装到自己的函数中,将来更改维护更容易,并且单独功能的函数会被隔离以仅更改该函数的行为。当我们使用局部变量的长函数在整个函数中持续存在时,由于函数的紧耦合,很难在不导致其他地方变化的情况下重构函数。
注释掉的代码
注释掉的代码应该被视为roadkill。不要看它,不要闻它,不要问它从哪里来,只是摆脱它。保持它的时间越长,其余代码闻到的时间就越长......
/ *
public void oldFunction(){
noOneRemembersWhyIAmHere();
tryToUnCommentMe();
iWillProbablyCauseABuildFailure();
HAHAHA();
}
* /
尽管删你不删别人更不敢删。如果你以后需要它,你可以随时检查版本控制系统,因为你肯定用了VCS,对吗?(如果不是当我没说)
TODO注释
不要写TODO注释,而不仅仅是......做到了吗?大多数时候这些注释都会被遗忘,后来可能变得无关或错误。当另一个程序员稍后看到TODO注释时,他们如何知道是否还需要这样做?
不过偶尔TODO注释是好的,如果你正在等待另一个队友的合并(一般不会太久)。就可以这么做,直到你可以进行修复并提交它。
“当你觉得有必要写注释时,首先要尝试重构代码,以便任何注释都变得多余。” - Martin Fowler
注释的谎言
当Jimmy在他写的新功能上面打上注释时,他认为他正在帮助任何看到他的代码的未来开发人员。其实呢他真正在做的是设置一个陷阱。他的注释可能是弥天大谎(没有双关语意图)蛰伏数月或数年没有被触及,只是等待成为一个令人讨厌的陷阱。然后有一天,在数百个重构和需求变更之一中,他的注释从一些遥远的模块中失效,但是仍然在错误的引导着无数的接盘侠。
当你更改一行代码时,你怎么知道你更改的代码会不会使其他地方的注释无效?没有办法知道
public class User {
...
//它包含用户的名字和姓氏
String name;
...
}
然后,需求更改,他们希望将名称拆分为firstName和lastName。
public class User {
...
// 它包含用户的名字和姓氏
String firstName;
String lastName;
...
}
注释现在已经错了。你可以更新注释以反映更改,但是你是否真的想在每次更改后手动维护所有注释?你是开发人员,而不是文档。
但是这个注释很容易被注意到并且没有问题需要改变。但是你很难保证在程序的其他地方,会不会也注释了这个参数name是用户的名字和姓氏。更改一小块地方的代码,可能会让很多的代码注释都失效。
让我们看另一个例子:
//根据状态处理员工
void processEmployees(){
...
List < Employee > employees = findEmployees(statusList);
...
}
//这会按状态列表查找Employees
List < Employee > findEmployees(List < String > statusList){
...
}
然后有人被要求更改函数findEmployees,以便通过名称列表而不是状态列表查找员工。
//根据状态处理员工
void processEmployees(){
...
List < Employee > employees = findEmployees(statusList);
...
}
//这会按状态列表查找Employees
List < Employee > findEmployees(List < String > nameList){
...
}
首先,上面的注释findEmployees已经失效,因此需要更改。没问题,对吧?错了。
processEmployees上面的注释也已失效,因此也需要更改。还有多少其他评论被这个小型重构改成无效?这一次更改在源代码中创建了多少注释谎言?
替代方案:
void processEmployees(){
...
List < Employee > employees = findEmployeesByName(nameList);
...
}
List < Employee > findEmployeesByName(List < Name > nameList){
...
}
如果你准确而准确地命名你的函数,则不需要注释,并且你不会在代码中散布谎言。
“代码永远不会说谎,注释会。” - 罗恩杰弗里斯
什么时候需要注释呢
我知道很多开发人员都是代码注释的死硬支持者,对他们来说,我必须承认有时注释是可以的。不过你每写一段都应当有充足的理由
复杂表达式
如果您有复杂的SQL或正则表达式语句,请继续编写注释。在代码中干净利落地表达诸如此类的陈述可能很困难。在这些表达式上面添加注释可以帮助其他开发人员更好地理解您的代码。
// 格式匹配kk:mm:ss EEE,MMM dd,yyy
Pattern timePattern = Pattern.compile("\\d*:\\d*:\\d* \\w*, \\w*, \\d*, \\d*");
注释警告
如果你需要警告其他开发人员这段代码可能发生的bug,可以在此代码附近留 * 释。这些注释可以充当代码中神秘行为的先兆,并为你的代码增加价值。
意图澄清
如果你实在命名废,那就要为你没有能力写出富有表现力的代码而负责,并写 * 释表明自己的意图。
如果你必须撰写注释,请确保它是本地的。远离其引用的非本地评论注定会失效并变成谎言。引用函数或变量的注释应直接位于其上方。警告注释可以在它引用的代码的上方或旁边。如果您的IDE支持注释突出显示,请使您的警告注释从其余代码中脱颖而出。
最后
我已经建立了对代码注释的感受。我鄙视他们,但我知道有时他们是需要的。
所以,请停止写这么多注释。
本文是作者在推特上看到国外一位大神 布莱恩·诺兰德 的论述,深以为然因此翻译后加以修饰进行分享的。希望今后自己的代码也能像散文一样优雅。
来源:https://juejin.im/post/5cf60bc8f265da1baa1e609e
猜你喜欢
- 1: Nacos搭建可以参考 https://www.jb51.net/article/196842.htmSpringCloud 版本&l
- 今天在群里看见有人问了这个问题,那就把我自己总结的知识拿出来与大家分享一下吧..当然可能还有什么不对的地方,希望指出:***msbase.j
- 一、template下文件不允许直接访问1、查资料得知:springboot项目默认是不允许直接访问template下的文件的,是受保护的。
- java 获取字节码文件的几种方法总结在本文中,以Person类为例,将分别演示获取该类字节码文件的三种方式,其具体思想及代码如下所示:pu
- @Autowired使用构造函数注入public Class Outer { private Inner inner; @Autowired
- 摘要:用spring-boot开发RESTful API非常的方便,在生产环境中,对发布的API增加授权保护是非常必要的。现在我们来看如何利
- 在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面是线程的创建更加规范,可以合理
- 一、Java把这些不同来源和目标的数据都统一抽象为数据流。Java语言的输入输出功能是十分强大而灵活的。在Java类库中,IO部分的内容是很
- 我这里有一个需求需要修改Person类中的一个属性上的注解的值进行修改,例如:public class Person { private i
- 前言:我们每天都在编写Java代码,编译,执行。很多人已经知道Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.cl
- 前言:页面静态化其实就是将原来的 * 页(例如通过ajax请求动态获取数据库中的数据并展示的网页)改为通过静态化技术生成的静态网页,这样用户
- Code CacheJVM生成的native code存放的内存空间称之为Code Cache;JIT编译、JNI等都会编译代码到nativ
- 将网络资源url转化为File文件将互联网上的http开头的url资源,保存到本地。 private File getNetUrlHttp(
- 可以用于简单的过期订单取消支付、7天自动收货场景中1、Spring Boot整合redis 参考https://www.jb51.net/a
- 前言记一次为了节省代码没有在方法体中声明HttpServletRequest,而用autowire直接注入所钻的坑结论:给心急的人。 直接在
- 以下总结是2016/3/23在做一个网站时遇到的一个功能模块,现在将总结从为知笔记上搬家到CSDN,与大家共享,欢迎指正。0.准备工作 0.
- 需要用到 java 写一个 ftp 的工具,因为只有一点点 java 基础,但是由于好几年不用,几乎算是不会了,只好一点点来搞,还好能捡起来
- Swagger以及knife4j基本使用Swagger 介绍:官网:https://swagger.io/Swagger是一个规范和完整的框
- 在去年的时候,在各种渠道中略微的了解了SpringBoot,在开发web项目的时候是如何的方便、快捷。但是当时并没有认真的去学习下,毕竟感觉
- 需求是需要在TextView前端加入一个标签展示。最终效果图如下:根据效果图,很容易就能想到使用SpannableStringBuilder