在我们平时刷题的时候,你可能会写过很多诸如
int a,b,c
int [] arrays=new int arrays[10];
if((numbers > 10 && flag == 'true') || flag =='false')
这种代码,对于我们自己练习编程或者解决一个算法题,当然没有问题。但是如果是在一个工程中,尤其是几十上百人维护了几年的工程中,还使用这种写法,倾泻自己天马行空的才华,保证leader不打死你哦。
所以,对于代码的整洁性,可读性,自古以来就有很多大神做出过总结,比如这本《clean code》,中文名叫做《代码整洁之道》,今天,我们就来看看吧。
命名命名思想
首先就是命名,命名可以说是一切程序的基础,如果用三个字来形容那就是——“有意义”。
你要做到,当一个人看到你的命名,就知道这个变量/函数是干什么的。
来看这一段代码:
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList)
if (x[0] == 4)
list1.add(x);
return list1;
}
这段代码非常简洁,但是非常模糊,我们不知道theList到底是什么,为什么x==4作为判断,list1又是什么?
现在我们来改一改:
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell : gameBoard)
if (cell[STATUS_VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
复制代码
从上面的代码中,我们可以马上看出来,flaggedCells表达的是标致cell,而cell位于gameBoard中,就是一个游戏面板,只需要再通过文件名,知道这是一个【扫雷游戏】,那么cell就是每一个格子,if语句中就是判断每一个格子是否被点击过,如果是,就添加到flaggedCells中,我们显然知道他想要干什么——搜集玩家点击过的格子并返回。
但是,上面的代码吗使用的是 int 数组,比如int[] cell,每一个数组内部的数表示cell的状态,可是cell并不需要那么多状态,而且这样导致每次使用这个状态的时候都要重新定义数组,状态是cell的一个属性,所以,完全可以定义一个cell的类,将他的状态封装进入。
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
这样子很清晰看出,gameBoard由Cell组成,从gameBoard中取出标记过的cell放入flaggedCells中,这样的代码是不是感觉浑然天成,自然而然呢?
命名规范
通过以上事例,你应该理解在写工程项目时,诸如数组,链表,字典这些底层结构应该要封装在User,Cell,Address这种类中,使用的时候直接使用这些类即可,这是一种大局观的思维,现在我们来做一些较为细节的落地规范。对于什么驼峰命名,匈牙利命名我相信你不会陌生,但是我在这里再强调两个地方。
第一原则
短小
如果还有第二原则,那还是短小。
短小到什么程度,最多20~30行吧,
所以要求,一个函数,只做一件事情,
什么叫做只做一件事情呢?
就拿处理数据来说,我们说【处理用户数据】是一件事,你也可以说是做了三件事:
当然,这个例子有点抬杠的意思,不过反映的现实是,代码中各种逻辑往往你中有我,我中有你,到底一件事情的边界在哪里?
这个因人而异,我只能提出书中的方法。
你可能会说,对于像if,while,switch这种语句,往往动辄十几行,短不了啊!
首先,这个原则不是铁律,实在太长也没办法。
其次,对于这些语句,完全可以讲里面的逻辑做一个封装,比如这种:
public static String renderPageWithSetupsAndTeardowns(
PageData pageData, boolean isSuite) throws Exception {
if (isTestPage(pageData))
includeSetupAndTeardownPages(pageData, isSuite);
return pageData.getHtml();
}
if 语句之后做了一件includeSetupAndTeardownPages的事情,不仅极大增强了可读性,而且代码短了不少。
关于参数
当你看jdk源码或者是android源码的时候,你会发现他们经常做调用,尤其是同名的方法重载,在看《算法》这本书的时候,也是,比如关于快速排序的:
public static void quickSort(int [] arrays){
quickSort(arrays,0,arrays.length-1);
}
private static void quickSort(int [] arrays,int left,int right){
...
}
我在一开始接触的时候,觉得虽然好看,但未免麻烦,随着经验的提升,这是一种非常好的编程习惯。
首先,对于用户来说,他想要进行快排,想传的只有数组,左右边界都包含进去了,你为什么要他多传参数?同时第一个方法使用的是public,就表示这是暴露给用户的。
其次,第二个方法在第一个方法中被调用,用private保护了起来,避免了无数麻烦。
所以,越是业务层的逻辑,越要写参数少的代码,如果参数必须要很多,那就用一个private的函数封装起来,你让用户拥有一百个参数输入,对他来说,那不是自由,那是灾难。
苹果和微信的使用体验就是将这种哲学贯彻到极致的代表。
注释最好的注释,就是代码本身。
用代码能解释清楚的事情,尽量少用注释,如果注释太多,只能证明代码写得烂……
不过有些地方还是有必要写代码的。
位置
最好写在方法顶部,不插入到实际代码中,比如那个面试几乎必问的String中的equals方法,源码如下:
...
* @see #compareTo(String)
* @see #equalsIgnoreCase(String)
*/
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = length();
if (n == anotherString.length()) {
int i = 0;
while (n-- != 0) {
if (charAt(i) != anotherString.charAt(i))
return false;
i ;
}
return true;
}
}
return false;
}
警示的注释
这里可以写一些告诫他人的代码,让后来的接盘侠能够引起重视。
//When I wrote this, only God and I understood what I was doing
//Now, God only knows
简单说就是,下面的代码一定有用,但是我也看不懂了,你别碰O(∩_∩)O哈哈~
在知乎上看到还有这样的。
//如果这段报错,你在机器上装一个360安全卫士,相信我我
以为是开玩笑,结果装了就真的好了。这个是前人留给我的。
//这个服务有问题的话,你可以问某某某,这段是他写的。
这个是在我离职交接时写的,出卖了未离职的一个同事。
//执行成功后发送一条通知短信,稳定后注释掉
手机号是写死的,我看到这段的时候还没有注释,这一年每天凌晨他都能收到短信。
这些人水平怎么样另说,但是对于后人还是用心的。
TODO注释
这个就不说了,很常用。
看完了有用的注释,我们来看看没用的注释
多余的
// Utility method that returns when this.closed is true. Throws an exception
// if the timeout is reached.
public synchronized void waitForClose(final long timeoutMillis)
throws Exception
{
if(!closed)
{
wait(timeoutMillis);
if(!closed)
throw new Exception("MockResponseSender could not be closed");
}
}
这就是把代码做了什么又描述一遍,没有任何意义。
被注释掉的代码
InputStreamResponse response = new InputStreamResponse();
response.setBody(formatter.getResultStream(), formatter.getByteCount());
// InputStream resultsStream = formatter.getResultStream();
// StreamReader reader = new StreamReader(resultsStream);
// response.setContent(reader.read(formatter.getByteCount()));
相信很多人都有这样的行为,在自己写算法题的时候用来测一测没有任何问题,但是对于后来者来说,他该怎么办?
他一定会想:也许是有用的呢?不然为什么之前要写。
但是他又看不懂或者觉得没必要看,于是就留了下,然后这样的代码就会越来越多,最终成为传说中的祖传代码。
不用心
注释要清楚,如果注释还要写注释来解释,根本没有意义。
比如下面这个,为什么要用200?
/*
* start with an array that is big enough to hold all the pixels
* (plus filter bytes), and an extra 200 bytes for header info
*/
this.pngBytes = new byte[((this.width 1) * this.height * 3) 200];
对于多数人来说,命名函数与注释基本上就是程序的主要组成部分,能够处理好这三样就能写出非常好的代码了,当leader看到你的提交的时候,看到的是如此优雅的代码,我想,他也会觉得是一种享受吧,就和诗歌一样。
作者:神鹰梦泽
链接:https://juejin.im/post/5f14043b6fb9a07e802055ce
来源:掘金
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved