21CTO社区导读:
本文来源于Quora,作者有两位,分别是Magnus Falk,有十年的开发和大型分布式系统。以及Matt Godbold20余年编程经验。
关于代码里的注释,我给大家说明的观点有如下。
概述
关于注释,首先它是有用的,通常情况下都需要。但是我认为,没有注释会更好。
下面来向各位解释我的理由:
我们的目标是编码。如要有人来通读它,需要完全清晰它的作用是什么。包括名称,结构,代码背后的思维等,这些应该最大化的开放、清晰、透明。
当我自己写注释时,会遵循增加更多详细以及增进的事情。我还期待我未来会再这些代码,不希望出现迷惑。所以我主要编写注释去增强我的未来。我想我此时的代码并不怎么清晰。
这并不是说注释不好。但是想让代码开发的更清晰,这个注释可以不需要。这是因为一些时候,注释与代码的功能未必匹配。
以下是代码的几个层次:
第一层:最差
复杂。代码不透明,而且什么注释也没有。
第二层:较好
复杂,不透明的代码与注释。现在大部分现实情况的程序员都是这样。
还有的程序员这样,实在是不该。
第三层:更好
简单,透明的代码与注释。
第四层:最好
代码写得如此营养丰富,注释根本不需要。
关于编码
我们在编码时,一定要干净,清晰,绝大多数还是需要用注释。因为大多数的程序还处于前三层,第四层是我们要达到的目标。
那么,什么时候需要注释?
- 当代码写的功能较复杂,或者看起来不易读,请使用注释;
- 如果执行结果不明显时,请使用注释;
- 当调用的方法上下文不明显时,请使用注释;
- 注释也不能只写晦涩的一行,为了写明白,有时候你要像写散文一样清晰优雅,无论是中文还是英语。
代码整洁之实践
以下是代码整洁的一些具体实践,和语言无关,供大家参考。
1.如何拒绝注释,用代码来阐述注释
反例如下:
/// <summary>/// !@#$%^&^&((!@#$%^&^&((!@#$%^&^&((/// </summary>/// <returns></returns>publicdecimal GetCash(]i{//!@#$%^&^&((var a=newList<int>(]i{2,3,10};var b =2m;var c =0m;//!@#$%^&^&((!@#$%^&^&((foreach (var p in a]i{c +=pb;}returnc;}
重构后的代码如下:
publicdecimal CalculateTotalCash(){var itemCounts=newList<int>(){2,3,10};var price=2m;returnitemCounts.Sum(p =>p*price );}
良好的代码命名完全可以替代注释的作用,如果你正在试图写一段注释,从某种角度来看,你正在试图写一段别人无法理解的代码。
当你无法为你的方法起一个准确的名称时,很可能你的方法不止做了一件事,违反了(Do one thing)。特别是你想在方法名中加入:And,Or,If等词时。
2. 为布尔变量赋值
反例:
publicbool IsAdult(intage){bool isAdult;if(age >18){isAdult =true;}else{isAdult=false;}returnisAdult;}
重构后:
publicbool IsAdult(intage){var isAdult =age >18;returnisAdult;}
3.双重否定的条件判断
反例:
if(!isNotRemeberMe){}
重构后:
if(isRemeberMe){}
不管你有没有见过这样的条件,反正我见过。见到这样的条件判断,我顿时就晕了。
4.拒绝HardCode,拒绝挖坑
反例:
if(carName =="Nissan"){}
重构后:
if(car ==Car.Nissan){}
既然咱们玩的是强类型语言,咱就用上编译器的功能,让错误发生在编译阶段
5.拒绝魔数,拒绝挖坑
反例:
if(age >18){}
重构后:
constintadultAge =18;if(age >adultAge){}
所谓魔数(Magic number)就是一个魔法数字,读者完全弄不明白你这个数字是什么,这样的代码平时见的多了
6.复杂的条件判断
反例:
if(job.JobState ==JobState.New
||job.JobState ==JobState.Submitted
||job.JobState ==JobState.Expired
||job.JobTitle.IsNullOrWhiteSpace()){//....}
重构后:
if(CanBeDeleted(job)){//}privatebool CanBeDeleted(Job job){var invalidJobState=job.JobState ==JobState.New ||job.JobState ==JobState.Submitted ||job.JobState==JobState.Expired;var invalidJob =string.IsNullOrEmpty(job.JobTitle);returninvalidJobState||invalidJob;}
有没有豁然开朗的赶脚?
7.嵌套判断
反例:
var isValid =false;if(!string.IsNullOrEmpty(user.UserName)){if(!string.IsNullOrEmpty(user.Password)){if(!string.IsNullOrEmpty(user.Email)){isValid=true;}}}returnisValid;
重构后:
if(string.IsNullOrEmpty(user.UserName))returnfalse;
if(string.IsNullOrEmpty(user.Password))returnfalse;
if(string.IsNullOrEmpty(user.Email))returnfalse;returntrue;
第一种代码是受到早期的某些思想:使用一个变量来存储返回结果。事实证明,你一旦知道了结果就应该尽早返回。
8.使用前置条件
反例:
if(!string.IsNullOrEmpty(userName)){if(!string.IsNullOrEmpty(password)){//register}else{thrownewArgumentException("user password can not be empty");} }else{thrownewArgumentException("user name can not be empty");}
重构后:
if(string.IsNullOrEmpty(userName))
thrownewArgumentException("user name can not be empty");if(string.IsNullOrEmpty(password))
thrownewArgumentException("user password can not be empty");//register
重构后的风格更接近契约编程,首先要满足前置条件,否则免谈。
9.参数过多,超过3个
反例:
publicvoidRegisterUser(
string userName
,string password
,string email
,string phone
){}
重构后:
publicvoidRegisterUser(User user){}
过多的参数让读者难以抓住代码的意图,同时过多的参数将会影响方法的稳定性。另外也预示着参数应该聚合为一个Model
10.方法签名中含有布尔参数
反例:
publicvoidRegisterUser(User user,bool sendEmail){}
重构后:
publicvoidRegisterUser(User user){}publicvoidSendEmail(User user){}
布尔参数在告诉方法不止做一件事,违反了Do one thing
10.写具有表达力的代码
反例:
privatestring CombineTechnicalBookNameOfAuthor(List<Book>books,string author){var filterBooks =newList<Book>();foreach (var book in books){if(book.Category==BookCategory.Technical &&
book
.Author
==author
){filterBooks
.Addbook]size=14;
}}var name
="";foreach
var book in filterBooks]size=14{name
+=book
.Name
+"|";}returnname
;}
重构后:
privatestring CombineTechnicalBookNameOfAuthor(List<Book>
books
,string author
){var combinedName
=books
.Whereb =>b.Category ==BookCategory.Technical]size=14.Whereb =>b.Author ==author]size=14.Selectb =>b.Name]size=14.Aggregatea,b]size=14=>a
+"|"+b
);returncombinedName
;}
相对于命令式代码,声明性代码更加具有表达力,也更简洁。这也是函数式编程为什么越来越火的原因之一。
四.关于DRY
平时大家重构代码,一个重要的思想就是DRY。我要分享一个DRY的反例:
项目在架构过程中会有各种各样的MODEL层,例如:DomainModel,ViewModel,DTO。很多时候这几个Model里的字段大部分是相同的,于是有人就会想到DRY原则,干脆直接用一种类型,省得粘贴复制,来回转换。
这个反例失败的根本原因在于:这几种Model职责各不相同,虽然大部分情况下内容会有重复,但是他们担当着各种不同的角色。
五.利用先进的生产工具
以Visual Studio插件中的Reshaper为例,本文列举的大部分反例,Reshaprer均能给予不同程度的提示。经过一段时间的练习,当Reshaper对你的代码给予不了任何提示的时候,你的代码会有一个明显的提高。
截图说明Reshaper的提示功能:
光标移动在波浪线处,然后Alt+Enter,Resharper 编辑器会自动对代码进行优化。
[size=16]小结[/size]
我们希望的目标是改变自己的编码风格,通过自我完善,还有类似上面的代码最佳实践,以减少使用注释。
代码中要尽可能清晰,能很清楚的表明意图——即代码透明。想需要提高代码的可读性,且不借助注释,必须改变代码的编写的方式。
也欢迎大家留言评论。
编译:21CTO社区
作者:Magnus Falk
来源:https://www.quora.com/Are-comm ... thing