如何写出好代码
这个题目把我自己都看傻了,因为仔细想想,这不是一个命题,是对代码的思考,对细节的推敲和打磨。写好代码是一门学问,还是一种修行。
以前是公众号(JackieZheng)和博客同步更新,尤其是技术类文章。但是最近在公众号上写的比较多,因为在那我可以想写多少写多少,随时随地记录下自己的心得,还有勉励自己的鸡汤或是毒鸡汤。
以后应该会阶段性把公众号的文章总结出来,写成一篇博客,想了想,这样比较符合这两个平台的特性。
从《阿里官方Java代码规范标准》说起
以前对于代码规范的理解和积累都是琐碎的,有时候从网上的文章看到如何命名,有时候从同行那里听到如何进行代码格式化。直到年前老大让我看看《阿里官方Java代码规范标准》,草草的过了遍,掠过某些点的时候有种被击中的感觉(哎呀,这条规范我之前不是一直都在理所当然的违背么,恩,理所当然),有些点看了还是有些麻木,大概是因为还不知道正确的做法是什么。
一次被老大review code的过程中发现了自己的代码中还是问题很多,最近也正好在看《代码整洁之道》,这里结合过往深刻教训以及公众号的总结说说如何写出好代码。
老掉牙的命名
稍微接触过一点点计算机的同学,都显然知道,变量的命名不能以数字打头,命名要有意义等等。
以前我认为只要不使用如i,j,k定义的变量名的工程师就是好的工程师,现在发现这是不够的。
- 魔法值
魔法值,如果你看过《阿里官方Java代码规范标准》,就肯定知道这个词的意思——使用了没有定义的值。比如maxPoint == 100
,看着没毛病啊,但是100这个值你这么用觉得合适么,人家还没有定义过呢。如果有个这样的声明语句private static final int MAX_BOILING_POINT = 100
,然后maxPoint == 100
写成maxPoint == MAX_BOILING_POINT
,看着是不是要清晰不少,规范了许多。当然,类似的例子还有很多。 - 1 l o 0
你来认认看,他们都是谁;
还有比如long id = 98765432l
,这个刚刚有那么一瞬间把我自己都骗到了,最后一位不是1啊;
我们知道对于double
和long
类型,都需要在定义的之后面加上d``L
,这是为了分别让double
类型和float
类型以及int
和long
区分开来。但是就像《阿里官方Java代码规范标准》中强调的那样,对于long
类型我们应该使用L
,应为如果写成long id = 5432l
就会出现上面一样的问题;
诸如此类的小细节都是坑,要么坑自己,要么坑队友。 - 命名有意义
i j k
这套确实看着让人头晕,那type data string
这套呢,看着好像好了不少,其实呢,还是换汤不换药,你能知道这type data string
分别代表什么意思么,那再换换salaryLevel totalSalary employeeName
,现在好多了,虽然变量长度增加了但是这样能够保证以后的你或者别人在阅读代码的时候能够读到那就明白到哪,而不是一遍又一遍的查看这个变量是怎么传过来的,源头的含义是什么。
函数
函数贵在短小。
- 当你读代码,看到一个方法,如果长到需要滚动鼠标来往下翻后续内容,再碰上没有区块或方法层注释的,就要抓狂了。这时候短小的优势就出来了,通过区区几行就能很好诠释你的函数干了什么事岂不是皆大欢喜。
如果能通过阅读方法名就知道这个方法是干什么的最好了。比如现在有方法叫做getData(...)
,还有一个叫做getPersonByName(...)
,显然后者强势碾压前者,起码我是这么认为的。这么写还有一个附加好处,就是省去了注释,因为方法名就是注释。
函数的目的就是为了告诉你它干了什么事,准确的说,它干了具体哪一件事。在设计模式的指导思想里面是有单一职责原则,对于函数也是一样。别指望一个函数能把所有的活都包了,那样大家都很累。比如
public PersonResult doSomething(String name, boolean isMarked, Person person) {
String[] names = name.split(",");
for(String name : names) {
//do some convert or others operations
}
if(isMarked) {
PersonResult personResult = new PersonResult();
personResult.setAge(person.getAget());
// ...
}
return personResult;
}
例子是刚刚想的,当然你可以写一个很长的全能doSomething
函数,但是读的人真的累。如果可以的话,这样是不是更好点
稍稍对比下,短小真的不是坏事,它能保持函数的内容在同一抽象层上,看着也舒服。
- 同一抽象层。这个概念很有意思,请教了老大有了比较好的理解。比如
public double getTotalSalaray() {
double baseSalaray = getBaseSalary();
//...
double salaryPart1 = 100;
double salaryPart2 = 200;
double salaryPart3 = 300;
double performanceSalary = salaryPart1 + salaryPart2 + ... + salaryPartN;N
return baseSalary + performanceSalary;
}
这个函数很单纯,真的只做了一件事,就是得到工资总额,包括基本工资和绩效工资。当我们看完第一行代码的时候,我们觉得赏心悦目,很好理解,如果你比较关系基本工资的细节,大可进入getBaseSalary()
方面里面一看究竟,可是在后面的绩效工资部分,就显得有些画风不对,我们不需要着这样的细节处理,我们更渴望与获取基本工资一样的抽象即可,好比这样
public double getTotalSalaray() {
double baseSalaray = getBaseSalary();
//...
double performanceSalaray = getPerformanceSalary();
return baseSalary + performanceSalary;
}
注释
有多少码农曾经一直被告诫要多些注释,写好注释,起码我之前是这样的被叮嘱的。但是看完《代码整洁之道》,发现注释不是什么好东西,起码没有我们想的那么好。
- 注释的副作用
谁也没法保证能一气呵成写完代码,而且永远不会有变动,所以我们需要经常变动代码,但是绝大多数时候,我们却由于各种原因忽视了变动注释。时间久了,代码和注释就不配套了,容易产生误解读。
在写下详细代码之后,又开始为这块代码加上注释,但是或许因为脑瓜子缺氧或是开小差了,写的注释与代码表达的逻辑不符,这样会让读者抓耳挠腮,匪夷所思。比如
public void getData(boolean isAdmin) {
// if isAdmin is true, get isGroup, if isGroup is true then doSomething
if(!isAdmin) {
boolean isGroup = PermissionUtil.getIsGroup();
if(isGroup) {
...
}
}
}
因为有很多判断,相信你应该会看一眼注释来帮助自己更好的理解这段代码,但是仔细一推敲,发现代码中的逻辑应该是isAdmin
为false
的时候,才会去获取isGroup
的值,而非像注释中提到为true
的时候。
- 有时候我们需要注释
必要的Javadoc,好比
/**
*get Person entity by id
*/
...
帮助澄清信息,比如
//example: www.test.childtest.com/v5/666
...domain + childDomain + version + id...
警示信息,对于一些代码的使用有些特殊场景要求,可以通过注释标示出来。
TODO 这个大家应该会经常使用吧,用来标记一些我们即将要实现的功能或者带扩展的功能。
今天就到这,后面有机会再整理。
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。