为什么大公司都注重代码规范, 今天就跟大家唠唠代码规范的好处和重要性。
你有没有想过,究竟是什么驱使你打开那些源代码去阅读的呢?或者说你在什么情况下会打开一段源代码阅读呢?
第一,你可能想了解一段代码的逻辑,比如,你在调用别人的代码时,感觉输出结果怪怪的,想进去一探究竟。
第二,你可能想要学习一下大神写的代码的的核心点。
第三,你运行这段代码时遇到了Bug,你要进去找到并修改它。或者想更改一下这段代码的某个地方。
可以看出,我们在阅读源代码的目的,都不是要把全部的代码阅读一遍,而是快速的找到我们需要的那个点,或者找出一条脉络。
如果说“代码是给人看的,顺便给机器运行以下”,那么写代码的人要怎样写,才能让阅读的人最快的找到自己需要的关键点呢?在查找算法中,最慢的就是,顺序查找,最快的是构建查找树进行查找。
如果有一个函数1000行,寻找某一行逻辑代码,平均需要500次,看的人头晕眼花。
如果他把函数切分成10个短函数,每个函数100行,找到一行代码平均需要5+50=55次。
如果他把每个短函数再分割成10个小函数,每个函数10行,查找平均需要 5+5+5=15次。
对于一个要查找500次才能找到的代码,大部分人直接就放弃了,不看了。你这代码不待见我。
但是对于一个平均看15次就能找到的代码,我们嗖嗖嗖就找到了。
这就是代码规范里最重要的一条: 不允许写长函数
而且把一千行的代码分割成10个函数写,我们只要看看这10个函数的名字,就能清晰的知道这段长代码的逻辑。一眼就把住了脉络。
当然这还需要我们的函数起得名字要能够说明这个代码时作什么的。很多人喜欢随便起个短小的名字然后写上注释,但是调用的时候大多数是不会看注释的,《代码整洁之道》中介绍说,最好的函数名,就是把你写的注释简化一下。比如
// sort the list and return the biggest one
int s(List arr){...}
阅读代码或者调用时,看到一个s函数,估计你和我一样会懵。
改成:
int sortAndGetBiggestElement(List arr){...}
这样就清晰明了了,这个函数名,在哪里调用都显得清晰明了。
这就是代码规范里最重要的第二条: 变量名函数名要能够表达变量和函数的作用。
作为码农的你,应该对快速排序不陌生吧。让你在没准备的情况下手撕一段快排,应该有点难度吧。
为啥呢,因为首先快速排序这个名字就得让我们想好一阵子,起得名字不具有解释性,别的排序法不快吗,还不如叫,左右分开再分治。
那Ok, 让你只看一遍快排的代码,看你能不能完全记得住。
public void fastSort(int[] arr){
int l=0,r=arr.length-1;
partition(arr,l,r);
}
void partition(int[] arr,int l,int r ){
if(l>=r) return;
int start=l,end=r;
int flag=arr[l];
int seat=l;
l++;
while (true){
while (l<=r && arr[r]>flag) r--;
if(l>r) break;
arr[seat] = arr[r];
seat = r;
r--;
while (l<=r && arr[l]<=flag)l++;
if(l>r) break;
arr[seat] = arr[l];
seat = l;
l++;
}
arr[seat]=flag;
partition(arr,start,seat-1);
partition(arr,seat+1,end);
}
这段代码中递归调用Partition 函数 ,先把[l,r]之间的元素,以第一个元素为标杆,分为两段,左边比标杆小,右边比标杆大,分完了以后再分别排序左右两段数据。
看一遍很难记住,主要原因是我的代码写的有问题,问题主要包括:
重构一下看看:
虽然重构了以后,代码主要逻辑在partition
, 就四句话,先判断退出条件,然后分割成两段splitTwoPart
,在依次递归调用左右两段。
这样我们就能快速记住,快速排序法的核心就是三句话,先分成两段,再递归分左一段和右一段。
public void fastSort(int[] arr){
if(arr==null) return;
partition(arr,0,arr.length-1);
}
void partition(int[] arr,int start,int end ){
if(start>=end) return;
int splitPoint= splitTwoPart(arr,start,end);
partition(arr,start,splitPoint-1);
partition(arr,splitPoint+1,end);
}
private int splitTwoPart(int[] arr, int left, int right) {
if(left>=right) return left;
int start=left,end=right;
int flag=arr[left];
int seat=left;
left++;
while (true){
while (left<=right && arr[right]>flag) right--;
if(left>right) break;
arr[seat] = arr[right];
seat = right;
right--;
while (left<=right && arr[left]<=flag)left++;
if(left>right) break;
arr[seat] = arr[left];
seat = left;
left++;
}
arr[seat]=flag;
return seat;
}
当然这里呢,我们对分割成两段还是不太直观的认识,把splitTwoPart
继续分割,在切分成两段的函数中,首先把所有参数放在了一个参数类中,然后循环内部做两件事,先从右向左找比标杆大的数。再从左向右找比标杆小的数。最后把标杆放在空位上。
private int splitTwoPart(int[] arr, int left, int right) {
IndexParam param=new IndexParam(left+1,right,left,arr[left]);
while (true){
if(!findBigOneFromRight(arr,param)) break;
if(!findSmallOneFromLeft(arr,param))break;
}
arr[param.seat]=param.flag;
return param.seat;
}
private boolean findSmallOneFromLeft(int[] arr, IndexParam p) {
while (p.left<=p.right && arr[p.left]<=p.flag) p.left++;
if(p.left>p.right) return false;
arr[p.seat] = arr[p.left];
p.seat = p.left;
p.left++;
return true;
}
private boolean findBigOneFromRight(int[] arr, IndexParam p) {
while (p.left<=p.right && arr[p.right]>p.flag) p.right--;
if(p.left>p.right) return false;
arr[p.seat] = arr[p.right];
p.seat = p.right;
p.right--;
return true;
}
class IndexParam {
public int left;
public int right;
public int seat;
public int flag;
public IndexParam(int left, int right, int seat, int flag) {
this.left = left;
this.right = right;
this.seat = seat;
this.flag = flag;
}
}
如果所有的算法都能把主干部分提炼出来,然我们一眼看穿,然后根据我们的记忆漏洞做针对性的补充,岂不美哉。
你看看以下几行代码,你估计以下他们可能是起什么作用的
int a=set(12);
c C;
fs(ar);
首先,第一行set(12)
应该是为什么设置值,但是返回的a是什么鬼呢? 看到这个,谁都得停下来挠挠头。
第二行c C;
是写反了吗,还是乱码了。看到这个是不是会停下来卡一下。
第三行,‘fs’是什么函数,看不懂,还得找到这个函数里面读读代码,或者注释。
这就是不规范的代码给我们的阅读带来的负担,就是要多花几秒钟才能理解,这几秒很可能就是你的忍耐力极线了。
第一行应该是设置某个变量后得到一个什么值,比如得到最小值。那就直接写成
int smallest=setAndGetSmall(12);
第二行,类要大写变量小写
class C{}
C c;
岂不是更符合正常人思维,还有类名字要把类的作用表达出来。
第三行,本来是快速排序函数,不用做简写,直接写成fastSort(arr);
可见,规范是最符合大多数人思维习惯的编程格式,所以,还是按套路出牌吧,不然你的下家头都要秃光了。
你最喜欢看到谁的代码呢?
当然是自己的代码,“常用的变量名、格式、工程结构”都是按你的习惯写的,肯定比较容易接受。
那怎么才能让你乐意去阅读别人的代码呢,可以让所有的程序员都按照你的“常用的变量名、格式、工程结构”得方式写代码是不是就不错呢?
这个当然不太容易实现,因为你的编程格式和习惯很多地方都不科学,也不合理。别人不愿意接受。
那还有一种方法,如果所有人都按照某一套编程规范写代码,你的规范风格和别人的规范风格都一样。这样你阅读别人的代码和阅读自己的代码就感觉一模一样了。
在团队合作中,自己的代码经常被队友吐槽,自己也常常吐槽队友的代码,那就让大家使用同一种代码规范写代码吧。做个和谐的码农小组。
我们的大脑就喜欢规规矩矩,符合常理的东西,符合规范的代码就像骑自行车,手把脚蹬,腰身保持平衡,都是我们的肌肉记忆。我们基本上就不用去思考,但是不符合规范的代码就像,突然让骑自行车的你,去学驾照,你恨不得把所有的注意力都用上,却还是筋疲力尽,难以控制,因为这不符合你的规范。
当然了,这个比喻不然恰当,学开车更像是新接触代码规范的你,虽然开始比较吃力,但是,等过几年,你的车龄足够了,就像路上所有的老司机,聊着天听着歌,还刷着微信(强烈不建议),依然游刃有余。符合规范的东西,我们的大脑会走捷径,不用过多思考,手到擒来。而不符合规范,我们就得调用全部精力才能对付。
当老司机看到刚学车的新手笨手笨脚的,不敢加速,还熄火,只想喷一句,“会不会写代码“
说了这么多代码规范的问题,那么哪里去找代码规范呢?
有两个资料比较好《阿里巴巴Java开发手册》,还有一本书《代码整洁之道》。
好代码,神清气爽,爱不释手,越写越好。
守规范,简洁直观,队友不喷,合作愉快。