《代碼整潔之道》總結(jié)
我們需要寫出整潔的代碼嗎
我是個(gè)6年開發(fā)經(jīng)驗(yàn)的java程序員,在我的職業(yè)生涯中,看到過(guò)不少讓產(chǎn)生罵人沖動(dòng)的代碼,當(dāng)然也寫過(guò)讓別人讓別人想罵人的代碼??。我曾很多次想改進(jìn)的我的代碼風(fēng)格,想讓它們更易懂,更嚴(yán)謹(jǐn),更整潔,更高效。但是終究還是寫了很多爛代碼。我總結(jié)了寫爛代碼的原因:
1、活多、時(shí)間緊。這可能是我們多數(shù)程序員寫爛代碼的直接原因,我們總抱怨需求緊時(shí)間少,功能實(shí)現(xiàn)就不錯(cuò)了,哪管代碼的好賴,或許這只是一個(gè)接口,真正的原因是我們懶于思考。如何去改進(jìn)代碼風(fēng)格。一個(gè)需求我們寫代碼的時(shí)間往往只占一小部分,就我個(gè)人而言,如果一個(gè)一天工作量的需求我寫代碼的時(shí)間往往不到2個(gè)小時(shí)。更多的時(shí)間一部分是搞清楚需求,另外一部分時(shí)間是和各種聯(lián)調(diào)測(cè)試,當(dāng)然還有同事隨時(shí)來(lái)找我打斷我的工作,重新開始寫時(shí)就需要重新 理下寫代碼思路。如果是修改之前的功能那耗費(fèi)最多時(shí)間的那就是閱讀這個(gè)功能之前的代碼。不管是調(diào)試代碼還是閱讀代碼,代碼的可讀性至關(guān)重要,易讀才是寫代碼高效的關(guān)鍵。多抽象出一個(gè)公用方法,變量名起的更貼切易懂寫真的占用很多時(shí)間嗎?好吧這些確實(shí)要占用你幾分鐘但當(dāng)你調(diào)試或者修改時(shí)或許可以幫你省下幾個(gè)小時(shí).
2、老代碼多人經(jīng)手無(wú)力回天。這種情況我也遇到過(guò)很多,但是當(dāng)我們放任這種情況不管,那這些代碼只能越來(lái)越糟、越來(lái)越難以維護(hù),最終變得不可維護(hù)。這種情況如果我們沒(méi)有足夠的信心去重構(gòu)一些方法,那我們至少需要讓 "讓營(yíng)地比你來(lái)時(shí)更干凈", 讓代碼簽入時(shí)比簽出時(shí)更干凈。
3、我也想寫出漂亮干凈的代碼,但是缺乏整體風(fēng)格,想一出是一出,難以為繼。這種情況也較為多見(jiàn),什么叫整潔的代碼,每個(gè)人理解不一樣,一千個(gè)程序員就有一千種對(duì)好代碼的理解。我的理解是寫出最多人更易讀懂的代碼就是好代碼。因?yàn)榇a更多是被讀的。不管是代碼的作者自己還是別的程序員。
基于以上我認(rèn)為整潔的代碼應(yīng)該是一個(gè)好程序員的基本素質(zhì)。那么如何寫出整潔的代碼呢?以下是我在讀完《代碼整潔之道》然后基于我7年寫代碼的經(jīng)驗(yàn)所做總結(jié),以期能成為一個(gè)真正的好程序員。
java是有個(gè)@Auther注解~是的,作者,我們是代碼的作者。我們要以作者心態(tài)自居,精心雕琢我們的代碼,以期讓我們的讀者在讀代碼時(shí)能賞心悅目。
如何能寫出好的代碼
好的代碼自己會(huì)說(shuō)話-讓命名更有意義
程序中通常需要命名的有三種:方法、變量、類。其中變量又分為局部變量和成員變量。首先我們看下下面這個(gè)變量
int d //過(guò)去的天數(shù);
是不是很懵逼,要知道這個(gè)d表示的什么意思,你要通讀這個(gè)變量的上下文才能知道。,即使你寫了注釋,當(dāng)你寫代碼時(shí)或讀代碼時(shí)是不是經(jīng)常要往上翻尋找這個(gè)d的注釋。如果你起一個(gè)有含義的變量名,那讀起來(lái)就大不一樣了,在代碼中直接就知道什么意思了。
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
起上面這樣的變量名是不是好多了,在代碼中遇到直接就知道這個(gè)變量是什么意思,甚至不用寫注釋也知道這個(gè)變量的意思,不用去翻看程序的上下文或者找這個(gè)變量的注釋。
再看下面的方法
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList)
if (x[0] == 4)
list1.add(x);
return list1;
}
看這樣的方法,你的頭頂是不是有很很多問(wèn)號(hào)。倘若我們把方法名和變量名起的有意義一些呢如下:
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell : gameBoard)
if (cell[STATUS_VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
這樣是不是好多了,僅僅把變量名改成的有意義寫,代碼就變得已讀了很多,此時(shí)還未改變代碼的結(jié)構(gòu)和嵌套。如果更進(jìn)一步我們不用 int 數(shù)組表示單元格,而是另寫一個(gè)類,這個(gè)類有個(gè)方法是否是Flagged的,遮掩住那個(gè)魔法變量則方法就能變成下面這樣。是不是更易讀了。
public List<Cell> getFlaggedCells() {
List<Cell> flaggedCells = new ArrayList<Cell>();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}
以上,我們只是簡(jiǎn)單了改變了代碼的名稱,代碼的易讀性大大提高。,代碼本身就是最好的注釋說(shuō)明。
好的代碼自己會(huì)說(shuō)話-準(zhǔn)確的命名、避免誤導(dǎo)
如果我們命名一個(gè)數(shù)組的話,那便不要使用accountList的命名,這會(huì)給讀者誤導(dǎo),誤以為這個(gè)變量是個(gè)List類型,所以用accountGroup或者arrayOfAccount甚至直接用accounts都會(huì)比accountList更好。不準(zhǔn)確命名的另外一個(gè)經(jīng)常發(fā)生的例子是含有數(shù)字的變量名,這種經(jīng)常發(fā)生我們要起一個(gè)變量名的時(shí)候發(fā)現(xiàn)這個(gè)變量重名了,我們經(jīng)常會(huì)再新變量后面加個(gè)數(shù)字來(lái)區(qū)分和原來(lái)的變量。
public static void copyChars(char a1[], char a2[]) {
for (int i = 0; i < a1.length; i++) {
a2[i] = a1[i];
}
}
上面的代碼如果改成下面的會(huì)不會(huì)好點(diǎn)
public static void copyChars(source a1[], destination a2[]) {
for (int i = 0; i < a1.length; i++) {
destination[i] = source[i];
}
}
命名還有一個(gè)經(jīng)常發(fā)生不準(zhǔn)確的命名的例子如下,這三個(gè)方法有什么區(qū)別呢,我們?cè)谑褂脮r(shí)應(yīng)該用哪個(gè)呢?這種命名區(qū)分毫無(wú)意義,調(diào)用者照樣需要去看方法的實(shí)現(xiàn)或者注釋來(lái)決定使用哪個(gè)方法?,F(xiàn)代聰明的ide會(huì)把所有的方法列出來(lái),然而你卻不知道該使用那個(gè)方法。
getActiveAccount();
getActiveAccounts();
getActiveAccountInfo();
消除魔法數(shù)字如下一段代碼,在代碼中直接使用數(shù)字,通常讀者不知道這個(gè)數(shù)字的含義,那當(dāng)別人來(lái)讀這段代碼的時(shí)候就要去找這個(gè)數(shù)字的含義
Order order = new Order();
order.setStatus(0);
這段代碼明顯是給訂單設(shè)置狀態(tài)。但是0是什么狀態(tài),如果沒(méi)有注釋或文檔的情況,需要找別的同事問(wèn)呢,即使有注釋和文檔是不是也需要通過(guò)這段代碼的上下文去找0的含義呢。如果我們把訂單狀態(tài)定義成一個(gè)enum或者常量:
Orderd order= new Order();
order.setStatus(ORDER_STATUS_PAY);
改進(jìn)后的代碼是不是很明顯就知道這個(gè)地方把訂單的狀態(tài)設(shè)置為支付狀態(tài)了。
好的代碼自己會(huì)說(shuō)話-類名和方法命名的更好實(shí)踐
如何才能讓我們的代碼命名更清晰簡(jiǎn)短有效呢。下面是《代碼代碼簡(jiǎn)潔之道》一書中做的總結(jié):
- 類名應(yīng)該是名詞或名詞短語(yǔ)。如 Customer、WikiPage、Account 和 AddressParser。避免使用 Manager、Processor、Data 或 Info 這樣的類名。類名不應(yīng)當(dāng)是動(dòng)詞。
- 方法名應(yīng)當(dāng)是動(dòng)詞或動(dòng)詞短語(yǔ),如 postPayment、deletePage 或 save。屬性訪問(wèn)器、修改器和斷言應(yīng)該根據(jù)其值命名,并依 Javabean 標(biāo)準(zhǔn)加上 get、set 和 is 前綴。
String name = employee.getName();
customer.setName("mike");
if (paycheck.isPosted())
重載構(gòu)造器時(shí),使用描述了參數(shù)的靜態(tài)工廠方法名。例如,
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
通常好于
Complex fulcrumPoint = new Complex(23.0);
因?yàn)镕romRealNumber描述了這個(gè)構(gòu)造器和別的構(gòu)造器的不同之處。
- 盡量給方法和類的命名都通俗易懂,不要為了耍寶起一些罕見(jiàn)的罕見(jiàn)的名字。畢竟代碼是用來(lái)讀的不是用來(lái)炫耀的。
- 每種概念都對(duì)應(yīng)一個(gè)單詞,并且一以貫之。如查詢方法經(jīng)常有g(shù)et、query、find、select、fetch等詞我們最好選一個(gè)詞來(lái)對(duì)應(yīng)查詢這個(gè)概念
- 盡量避免用含有多個(gè)意思的詞如add,既可以表示加法,又可以表示添加元素,有時(shí)會(huì)對(duì)讀程序的人產(chǎn)生誤導(dǎo)。
- 使用某種解決方案領(lǐng)域的專有名稱,如果你使用了監(jiān)聽器,可以把類名后綴寫成listener、如果使用訪問(wèn)者模式,可以用visitor作為類的后綴,如果使用了門面模式可用face作為后綴,這樣在別的程序員讀你的代碼時(shí)就會(huì)有個(gè)全局的概念,知道你設(shè)計(jì)代碼時(shí)的思路。更好的理解代碼。
- 如果不能使用計(jì)算機(jī)解決方案領(lǐng)域的名詞來(lái)命名,那就使用業(yè)務(wù)領(lǐng)域的名詞來(lái)命名吧。至少,負(fù)責(zé)維護(hù)代碼的程序員就能去請(qǐng)教領(lǐng)域?qū)<伊耍?strong>優(yōu)秀的程序員和設(shè)計(jì)師,其工作之一就是分離解決方案領(lǐng)域和業(yè)務(wù)領(lǐng)域的概念。與所涉問(wèn)題領(lǐng)域更為貼近的代碼,應(yīng)當(dāng)采用源自業(yè)務(wù)領(lǐng)域的名稱。