资深大佬告诉你,怎样才能写出漂亮高质量的JAVA代码?

怎样才能写出漂亮高质量的java代码?

一般来说代码质量由以下五个方面决定:编码标准、代码重复、代码覆盖率、依赖项分析、复杂度分析。我们分别分析一下这五个方面。

**编码标准:**一般每个公司都会有一套自己的编码规范,规范都会详细的要求类命名、包命名以及代码风格等。

**代码重复:**简单来说就是你需要规避大量重复代码,如果说你发现你有大量的重复代码,那就要考虑把这部分代码提取出来单独的封装成公共的方法或者组件了。

**代码覆盖率:**要清楚你的测试代码占总代码的覆盖率,这关系到代码的功能性和稳定性。

**依赖项分析:**代码依赖关系怎么样?耦合关系怎么样?是否有循环依赖?是否符合高内聚低耦合的原则?

**复杂度分析:**代码一点要做到至简,尽量规避多层循环或者多层的if else。

那么如何才能编写出优质代码呢?我相信每个开发人员都会有自己的理解。这里整理了一些方法,供大家参考。

1、 养成一个好习惯

有一个良好的习惯是很有必要的,这不仅仅是针对我们的工作,其他方面也是同样的道理。我们后面讲的内容大部分都可以归于养成一个好习惯的具体实现。

2、 规范你的代码

代码拥有统一的格式和规范,既便于代码的逻辑清晰,又便于维护,好的编码规范可以尽可能的减少一个软件的维护成本,甚至在将来移交给他人时,也能更容易理解你的代码,毕竟很少有一个软件从开始到结束,都由最初的开发人员来维护。

3、 不写过多参数方法

当你的方法参数超过5个时,你就应该考虑是否有无用参数了,过多参数不仅增加了理解难度,还容易出现运行时异常,更不利于维护。如果你觉得你必须要有这么多参数,那就把参数封装成对象吧,这样既方便管理参数,又提供了扩展性。

4.不写重复代码

当你的方法参数超过5个时,你就应该考虑是否有无用参数了,过多参数不仅增加了理解难度,还容易出现运行时异常,更不利于维护。如果你觉得你必须要有这么多参数,那就把参数封装成对象吧,这样既方便管理参数,又提供了扩展性。

5. 不写重复代码

重复代码=垃圾代码。如果你发现你有很多的重复代码时,正确的做法是把这部分代码提取出来封装成公共方法或者工具。

6、不做没有意义的事

我们开发的时候,常常会通过复制粘贴来实现一些功能,但是复制粘贴之后,会引入很多使用不到的东西,这些代码搁置在那边完全就是无意义的,可以删除。

7、正确摆放代码

除了要实现功能外,还要考虑的事情非常多,正确摆放代码位置就很重要。检查方法,看里面的实现逻辑是否应该放在这个名称的方法中;检查类,看里面的方法是否应该放在当前类中;检查工程,看里面的类是否应该放在这个工程里面。一层层检查,该发现代码有多少问题了吧。这有时候就是人的过程性思维导致的,从大的方面来讲是我们抽象的不够。

8、多为你的使用者考虑

做任何事情如果没有服务的对象,也就失去了它本身的意义,同样,编码也是如此。如果你是做框架做产品的,那么你面对的就是普通开发人员;如果你是做项目的,那么你面对的就是我们通常意义上的客户。不管你面对的是什么对象,一个好的出发点非常重要:多为你的使用者考虑。

9、合理利用检查工具

码完代码后,用上一些简单的静态检查工具,比如checkstyle、fingbug等,可以很方便的检查出你代码中格式、以及一些隐藏的漏洞。另外可以做下单元测试,让你的代码更健壮。

10、重构你的代码

重构能改善软件设计。

重构使软件更易理解。

重构有助于找到Bug。

重构有助于提高编程速度。

强烈推荐参阅《代码整洁之道》这本书,通过结合这本书的理论和技巧,在写代码的过程中不断重构,优化,你的代码质量就会逐步提高。

以下是我参阅这本书之后总结出来的写出高质量代码的关键要点,你可以参考,希望能够帮助到你。

一以贯之的命名原则:
如fetch,get,retrieve 都可以用作获取,拿到 的意思,但是我们要根据实际的应用场景,在通一个类或者模块中尽量只使用其中一个作为方法名的前缀,如getXXX

一词一意原则:
如add表示增加,可以在任何地方增加,如前面,中间,后面等等。但是如果有一个方法需要表示只能在末尾附加,此时就不应该使用add方法了,而应该用append方法,这个原则给一以贯之命名原则做了限定,即只有是代表同样的操作的时候才一以贯之。

使用解决方案领域名称:
尽量使用专业的领域名称

使用源自所涉问题领域的名称:
当程序员很难在现有代码中使用专业领域名称的时候,可以考虑使用源自所涉问题领域的名称,可以理解为业务,了解业务,对于命名也是非常有帮助。
其实好的程序员和软件设计师,所做的工作就是分离好解决方案领域和问题领域的概念,使得代码更加贴近于问题领域

好的函数命名—只做一件事
如果一个函数里面,包含了同一个抽象层级的多个函数,这样也是算只做一件事,但是如果包含了不同抽象层级的,那就是做了很多事,多年的编码经验告诉我们,函数只做一件事,比做很多事好维护的多!

如何保证函数只做一件事
首先是这样的,把做的事情进行分解,比如说 可以这样来进行分解:
要做一个到大功能x ,就要先做第一个中等功能xx,要做一个中等功能xx就得先做一个小功能xxx。然后做第二个中等功能yy,要做中等功能yy,就要先做小功能yyy…通过一层层分解,就能保证函数只做一件事了。

函数的参数尽量少
函数的参数和函数名不是一个抽象层级上的东西,会让别人阅读的时候感觉有点困难,在一个函数中尽量不要使用参数,或者只有一个参数,要是3个以上的参数,那确实有点多了,除非特别必要,不然真的不要写那么多参数,太多了 让人容易误解。还有一个非常值得注意的是不要使用参数作为返回值!!!一定要使用函数返回值,不然真的让人很难读懂你的代码!

一元函数的普遍形式
向函数传入单个参数,有两种普遍理由,第一种,如boolean fileExists(“myFile”),这种是判断输入参数 是什么,怎么了,等等。。第二种,如InputStream fileOpen(“myFile”),这种是把参数转换为某种其他的东西,再输出来! 这个是书写一元函数普遍需要遵循的原则,值得注意的是,如果函数需要对输入函数进行转换的时候,转换结果应该体现在函数的返回值上,而不是参数

函数的参数里面不要放标识参数(即boolean)
因为函数里面放了标识参数,那么该函数做的事情就不是一件事了,它必须要判断当标识参数是真的时候,该做xxx,当标识参数为false的时候,该做yyy,在这种情况下,由于它要做很多的事,所以很难给她命名!例如这个函数 render(boolean isSuit); 当读者看到调用该函数的代码时,render(true) 根本不知道这是干嘛的!正确的做法是应该把他们拆开成两个函数,如 renderForSuit() 和 renderForNoSuit() 这样 就一目了然。一个小经验,当给一个类或者函数,变量命名时感觉非常困难的时候,往往意味着我们分析,划分的模块有问题,粒度不够小,模块和模块,函数和函数之间有 很大的耦合,还需要我们再仔细分析,划分,设计!

参数对象的抽取
如果一个函数拥有两个及以上的参数时,往往可以意味着,其中的一些参数可以封装成对象了,因为这些参数是一起传递到函数里面的,所以有很大的可能性,这些参数本身就有关系,所以把他们封装成对象就是很自然的做法了!

例如:
MakeCircle(int x,int y,int radius);
可以改造成
MakeCircle(Point point , int radius);
这样就把x,y封装到一个对象去了,对代码的可读性有所提升!

函数命名–动词与关键字
较好的函数命名可以让使用我们代码的人很容易理解我们的代码,对于一元函数,应该使用动词+关键词(应该是名词)的组合形式,如 write(name)这个函数,相当令人认同,但是如果使用writeField(name)也许会更好,即动词+关键词的组合。

再比如 assertEqual(expected,actual) 这个方法,改assertExpectedEqualActual(expected,actual)也许会更好,这样别人再使用我的代码的时候就不用记住参数顺序啦!(不过我对此表示怀疑,因为现代的IDE是可以提示函数参数的,所以为了让函数名体现参数而写这么长的函数名,恐怕有点得不偿失,并且参数函数名+里面的参数已经体现了这个函数的意图,所以看起来也比较容易读!这个暂时放一边吧,没准以后再读的时候就不一样了)

函数不应该有副作用
例如一个函数
checkPassWord(userName,passWord){

If(userName.equals(“zrc”) && passWord.equals(“123”)){

This.initSession(); Return true; }

Else { Return false;

}

}

这个函数虽然名为checkPassWord ,意思是检查用户的密码,实则它不只是干了这么一件事,它还干了一件事是重新初始化Session,这显然是不可以的,因为当别人接手你的代码的时候,要是相信了你这个函数的命名,认为它只是检查密码而已,不知道这背后的还有一个副作用是重新初始化Session,那么就有可能丢失Session数据,这肯定是不可以接受的,所以 正确的做法应该是把这个副作用提出去,如果确实要重新初始化Session,那就老老实实搞在这个函数后面写就是了!

分隔指令与询问
函数要么做什么事,要么回答是什么事,二者不可兼得,要是两个都做往往会做的很混乱
如下面这些语句
If(set(“userName”,”zrc”){

doXXXX

}

这个 乍一看,还真不太清楚想表达什么? 这个set函数返回值代表什么?是代表判断设置属性username成功与否的标识?还是判断username 之前的值是否是zrc的标识呢?
不清楚,
更好的做法是如下:
If(attributeExist(“username”)){ Set(“username”,”zrc”); doXXX }
这样就一目了然了!

使用异常代替返回错误码
如下代码:
If(deletePage(page) == E_OK){
If(deleteHeader(page.header) == E_OK){
}else{

//做对应的处理
}
}else{
//做 对应的处理
}
使用错误码返回 会导致代码里面充满着if else 的判断。不利于代码的阅读
正确的做法是 使用异常来处理这些
如下代码:
Try{
deletePage(page);
deleteHeader(page.header);

}
Catch(e){
//做响应的处理
}
这样的代码就把错误的处理信息从代码的主路径中抽离出来,更加容易阅读!
错误处理只是一件事,抽离出try/catch代码块
如函数:
Void delete(page){
Try{
deletePage(page);
deleteHeader(page.header);
}

Catch(e){
//做响应的处理
In.close();
}
}
其实这个函数还不是最好的,毕竟delete 这个函数做了 删除了page和header 这件事之外,还做了一件判断异常的事情,违反了一个函数只做一件事的原则,所以可以把他们抽取出来如下:
Void delete(page){

Try{

deletePage(page);

deleteHeader(page.header);
}

Catch(e){
//做响应的处理
In.close();
}
}
经过这样的抽离之后,就可以看出 代码写的很优雅了!相当于delete函数只干一件事,就是判断是否有异常

给正在自学java或者即将学习的朋友们整理了一些赠送2019年最新Java精讲视频,PDF书籍教程,面试题,学习手册点击免费下载

最后再说一下,写代码其实跟写其他东西是一样的,就像写文章一样,你先想什么就写什么,然后再打磨它,初稿可能粗陋无序,那你就仔细斟酌,直到达到你心中的样子。

一开始乱很正常,但是要在后续不断的斟酌,思考,重构出你想要的样子,写代码不是一开始就按照规则来的,我想应该没人能做到,只有不断地写,不断地发现重复,不断打磨重构才能写出好的代码!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值