美化java代码,从合理注释开始

请停止代码注释

“干净的代码应该像写好的散文一样” - 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支持注释突出显示,请使您的警告注释从其余代码中脱颖而出。

最后

我已经建立了对代码注释的感受。我鄙视他们,但我知道有时他们是需要的。

所以,请停止写这么多注释。

本文是作者在推特上看到国外一位大神 布莱恩·诺兰德 的论述,深以为然因此翻译后加以修饰进行分享的。希望今后自己的代码也能像散文一样优雅。

(0)

相关推荐

  • Java代码注释规范详解

    代码附有注释对程序开发者来说非常重要,随着技术的发展,在项目开发过程中,必须要求程序员写好代码注释,这样有利于代码后续的编写和使用. 基本的要求: 1.注释形式统一 在整个应用程序中,使用具有一致的标点和结构的样式来构造注释.如果在其它项目中发现它们的注释规范与这份文档不同,按照这份规范写代码,不要试图在既成的规范系统中引入新的规范. 2.注释内容准确简洁 内容要简单.明了.含义准确,防止注释的多义性,错误的注释不但无益反而有害. 3.基本注释(必须加) (a) 类(接口)的注释 (b) 构造函

  • Java代码注释规范(动力节点整理)

    代码注释是架起程序设计者与程序阅读者之间的通信桥梁,最大限度的提高团队开发合作效率.也是程序代码可维护性的重要环节之一.所以我们不是为写注释而写注释.下面说一下我们在日常开发中使用的代码注释规范,供大家参考下. 1. 注释形式统一 在整个应用程序中,使用具有一致的标点和结构的样式来构造注释.如果在其它项目中发现它们的注释规范与这份文档不同,按照这份规范写代码,不要试图在既成的规范系统中引入新的规范. 2. 注释内容准确简洁 内容要简单.明了.含义准确,防止注释的多义性,错误的注释不但无益反而有害

  • Java的文档注释之生成帮助文档的实例

    示例: /** * Title: Person类<br/> * Description:通过Person类说明Java中的文档注释<br/> * Company: *** * @author *** * @version 1.0 */ public class Person { /** * 这个是Person类的构造方法 * @param name Person 的名字 * */ public Person(String name) { //执行语句: } /** * 这是read

  • 详解IDEA自定义注释模板(javadoc)

    最近要开始做项目了,项目规定了方法注释模板,那么idea是如何自定义注释模板的呢? 有两种解决方案: 1.安装Jindent插件,好处是自动生成模板,但是很可惜本人安装失败,设置好以后不知道为什么无法Apply,可能是我的版本问题,失败的同学请看方案二. 2.Jindent插件下载:http://plugins.jetbrains.com/plugin/2170?pr=idea,也可以直接在idea中在线安装,类似eclipse的在线install2.使用idea自带的Live Template

  • 如何在java中正确使用注释

    Java提供了3种类型的注释: 单行注释(C++风格) 在Java中最简单的注释是单行注释.它以两个正斜杠开始并到行尾结束.例如: // this is a single-line comment x = 1; // a single-line comment after code 多行注释(C风格) Java同样提供跨越多行的注释类型.这种类型的注释以紧跟着一个星号的正斜杠开始,并以紧跟着一个正斜杠的星号结束.这种类型注释的开始和结束分界符可以在同一行里也可以在不同的行上.例如: /* Thi

  • IDEA插件之快速删除Java代码中的注释

    背景 有时,我们需要删除Java源代码中的注释.目前有不少方法,比如: 实现状态机.该方式较为通用,适用于多种语言(取决于状态机支持的注释符号). 正则匹配.该方式容易误判,尤其是容易误删字符串. 利用第三方库.该方式局限性较强,比如不同语言可能有不同的第三方库. 本文针对Java语言,介绍一种利用第三方库的方式,可以方便快速地移除代码中的注释. 原理 这个第三方库叫做JavaParser.它可以分析Java源码,并生成语法分析树(AST),其中注释也属于AST中的节点. 因此核心思路即为: J

  • 美化java代码,从合理注释开始

    请停止代码注释 "干净的代码应该像写好的散文一样" - Robert C. Martin 不良代码的通病就是有很多注释.这是凌乱的源代码最明显的迹象. 每个程序员的目标应该是编写干净和富有表现力的代码,以避免代码注释.每个变量,函数和类的目的应该隐含在其名称和结构中. 当其他人读取您的代码时,他们不应该阅读注释以了解你的代码正在做什么.命名良好的类和函数应该引导读者通过你的代码,就像一本写得很好的小说一样.当读者看到一个新的类或功能时,他们不应该对他们在里面看到的东西感到困惑难以理解.

  • PHP,ASP.JAVA,JAVA代码格式化工具整理

    PHP代码格式化工具http://www.waterproof.fr/products/phpCodeBeautifier/最近修改一个代码,里面完全没有缩进,用这个能够格式化一下.相当于以前c 的indent顺便写一下其他语言的c, c++ c# ==> indent Astylejava ==> astyle Jalopy Jacobe ImportScrbberphp ==> phpCodeBeautifierperl ==> perlTidypython ==> Py

  • Spring学习笔记1之IOC详解尽量使用注解以及java代码

    在实战中学习Spring,本系列的最终目的是完成一个实现用户注册登录功能的项目. 预想的基本流程如下: 1.用户网站注册,填写用户名.密码.email.手机号信息,后台存入数据库后返回ok.(学习IOC,mybatis,SpringMVC的基础知识,表单数据验证,文件上传等) 2.服务器异步发送邮件给注册用户.(学习消息队列) 3.用户登录.(学习缓存.Spring Security) 4.其他. 边学习边总结,不定时更新.项目环境为Intellij + Spring4. 一.准备工作. 1.m

  • JAVA代码开发规范

    一.开发工具规范: 1. 开发工具经项目负责人调试后统一确定. 2. 开发工具一经确定不允许集成任何非统一插件,若有需要,经项目负责人同意后统一为 项目组成员添加. 3. 开发工具的编码格式不允许修改. 二.排版规范: 1. 关键词(或变量)和操作符之间加一个空格. 例如:int iCont = 1;//操作符和值之间有一个空格. 2. 相对独立的代码块与块之间加空行. 例如:两个方法之间需要用空格隔开. 3. 较长的语句.表达式等要分成多行书写. 4. 长表达式要在低优先级操作符处划分新行,操

  • jsp中调用java代码小结

    原封不动的传送给客户端有两个小例外: 1. 如果想传送 <%或%>, 由于它跟jsp的特殊符号一致, 所以必须使用转义字符转义, <\% 或 %\>2. 如果想传送注释, 在 jsp 编辑页面中出现, 而在生成的html文档不出现, 那么我们要使用 <%-- --%>, 而 <!-- --> 这种形式会原封不动的传递给客户端. jsp 调用动态代码策略 使用 mvc, 由一个 servlet 负责处理最初的请求, 查找数据, 并将结果存储在 bean中, 然

  • 200行Java代码编写一个计算器程序

    发现了大学时候写的计算器小程序,还有个图形界面,能够图形化展示表达式语法树,哈哈;) 只有200行Java代码,不但能够计算加减乘除,还能够匹配小括号~ 代码点评: 从朴素的界面配色到简单易懂错误提示,无不体现了"用户体验"至上的设计理念:代码异常处理全面合理.滴水不漏,代码缩进优雅大方,变量命名直观易懂:再结合长度适中简单明了的注释,程序整体给人一种清新脱俗之感.背后不难看出作者对学习的热爱以及对设计的苛求,工匠精神可见一斑,真可谓是大学数据结构学以致用的典范! 实现代码如下所示:

  • Java代码重用之功能与上下文重用

    我几乎不需要讨论为什么重用代码是有利的.代码重用通常使得程序开发更加快速,并使得 BUG 减少.一旦一段代码被封装和重用,那么只需要检查很少的一段代码即可确保程序的正确性.如果在整个应用程序中只需要在一个地方打开和关闭数据库连接,那么确保连接是否正常则容易的多.但我确信这些你已经都知道了. 有两种类型的重用代码,我称它们为重用类型: 功能重用(Action Reuse) 上下文重用(Context Reuse) 第一种类型是功能重用,这是最常见的一种重用类型.这也是大多数开发人员掌握的一种.即重

随机推荐