编写自文档化代码
版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。 https://blog.csdn.net/YhL_Leo/article/details/50545047
?
本系列文章由 @yhl_leo 出品,轉(zhuǎn)載請(qǐng)注明出處。
文章鏈接: http://blog.csdn.net/yhl_leo/article/details/50545047
?
嚴(yán)肅是寫作必備的兩個(gè)因素之一。另一個(gè),很不幸,是天分。 —— 歐內(nèi)斯特??海明威
個(gè)人覺得可以把一個(gè)人編寫程序所處的階段可以分為四個(gè):
- 識(shí)字:接觸和掌握了一些基本的編程知識(shí)(變量,類型,函數(shù),……)和語法(運(yùn)算,循環(huán),判斷,……);
- 造句:可以根據(jù)基本的知識(shí)和語法模仿甚至自主實(shí)現(xiàn)一些小的算法等;
- 自由表達(dá):熟悉掌握編程語言后,只要不太復(fù)雜的算法,似乎只要知道算法原理,就能實(shí)現(xiàn);
- 妙筆生花:此時(shí),你可能意識(shí)到代碼實(shí)現(xiàn)不再是你所追求的目標(biāo),創(chuàng)建優(yōu)秀的代碼對(duì)你而言更具吸引力和挑戰(zhàn)性。
創(chuàng)建優(yōu)秀的代碼意味著創(chuàng)建良好的文檔化的代碼。我們編寫代碼的原因是要表達(dá)一套清晰的指令——不僅僅是對(duì)電腦,也是對(duì)那些以后需要維護(hù)或拓展這些指令的可憐傻瓜們。所以,千萬不要去膜拜把原本簡單易懂的代碼寫得反人類的所謂大神!現(xiàn)實(shí)世界中的代碼從來沒有在編寫完成后就被遺忘掉。在軟件產(chǎn)品的生命周期內(nèi),這些代碼將不斷地被修改,拓展和維護(hù)。想要做到這一點(diǎn),我們需要說明指導(dǎo),即一個(gè)文檔化的用戶指南。
對(duì)于代碼的文檔化,人們通常的做法是編寫大量的關(guān)于代碼的文檔或者在代碼中添加大量的注釋,這兩種方式都是明智的。
實(shí)際上,這兩種方法都是無稽之談。大多數(shù)程序員對(duì)文字編輯器唯恐避之不及,對(duì)于編寫太多注釋也是相當(dāng)頭疼。編寫代碼是意見艱苦的工作,而將代碼文檔化更是艱苦異常。
對(duì)于支持文檔的系統(tǒng),常常面臨以下挑戰(zhàn):
- 我們不需要做額外的工作。編寫文檔非常耗費(fèi)時(shí)間,閱讀文檔也是這樣。程序員們更愿意將時(shí)間花在編寫程序上。
- 所有的獨(dú)立文檔都必須隨著代碼的更新而不斷更新。在大型項(xiàng)目中,這將是一項(xiàng)可怕的工作。而不更新任何文檔將會(huì)導(dǎo)致危險(xiǎn)的錯(cuò)誤和產(chǎn)生錯(cuò)誤的信息。
- 大量的文檔是很難管理的。在大量的文檔中查找正確的文檔,或者尋找在一個(gè)文檔中可能多次出現(xiàn)的信息是不易的。就像代碼一樣,文檔必須接受版本的控制,必須確保所閱讀的文檔的版本與所處理的代碼的版本相對(duì)應(yīng)。
- 分布在不同文檔中的重要信息很容易被錯(cuò)過。
建議不要編寫需要外部文檔支持的代碼。這樣的代碼是脆弱的。要確保你的代碼本身讀起來就很清晰。
對(duì)于另一種方案——使用詳細(xì)的代碼注釋來使代碼文檔化——即使不是最糟的方法,也不是很好。一大堆毫無創(chuàng)造性的逐條注釋,會(huì)妨礙好代碼的產(chǎn)生。
避免這些,最好的方法應(yīng)該就是編寫自文檔化的代碼吧。
要知道唯一能完整并正確地描述代碼的文檔就是代碼本身。這并不自然而然地意味著代碼本身就是最佳的描述,但是在通常情況下,這是我們所能獲得的唯一文檔。
因此,你應(yīng)該想盡辦法使代碼成為良好的文檔,一種人人都可以讀懂的文檔。除了編寫代碼的作者外,必須還有更多的讀者可以理解代碼。編程語言是我們交流的媒介,清晰的交流至關(guān)重要。清晰的代碼就會(huì)獲得更高的質(zhì)量,因?yàn)槟惴稿e(cuò)誤的可能性會(huì)大大降低(錯(cuò)誤變得顯而易見),而且這樣的代碼維護(hù)成本也比較低。
自文檔化的代碼是可讀性很強(qiáng)的代碼。它本身就易于理解,而不需要依賴外部文檔。我們可以通過很多方式提高代碼的清晰度。其中一些技巧是非常基礎(chǔ)的,而且在我們學(xué)習(xí)編程之初就被訓(xùn)練加以掌握,而其他一些技巧則是從經(jīng)驗(yàn)中獲得。
編寫易于閱讀的代碼。人性化。簡單易懂。編譯器可以處理。
先看一段簡單的函數(shù)例子:
int fval(int i) {int ret=2;for (int n1=1, n2=1, i2=i-3; i2>=0; --i2){n1=n2; n2=ret; ret=n2+n1;}return (i<2) ? 1 : ret; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
這個(gè)示例非常接近現(xiàn)實(shí)。很多代碼看上去就是這個(gè)樣子的,雖然運(yùn)行沒有問題,結(jié)果也能輸出準(zhǔn)確,但是著實(shí)讓戰(zhàn)斗在前線的程序員們深受其害。再看一段自文檔化的代碼:
int fibonacci(int position) {if (position < 2){return 1;}int previouseButOne = 1;int previous = 1;int answer = 2;for (int n = 2; n < position; ++n){previouseButOne = previous;previous = answer;answer = previous + previouseButOne;}return answer; }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
你或許只要讀第一行代碼,就可以知道這段程序在做什么了~它本身沒有注釋,卻非常容易理解。一定要記住:注釋只會(huì)增加人們的閱讀量。不必要的注釋,往往使代碼變得毫無必要地?zé)┤?#xff0c;而且將來會(huì)使函數(shù)的維護(hù)變得更加艱難。了解這一點(diǎn)非常重要,因?yàn)榧幢闶亲钚 ⒆顑?yōu)美的函數(shù)也需要日后進(jìn)行維護(hù)。
傳統(tǒng)的觀念認(rèn)為,編寫自文檔化的代碼需要添加大量的注釋。良好的注釋編寫當(dāng)然是一項(xiàng)重要的技能,但是除此以外,還有很多重要的技能。事實(shí)上,我們應(yīng)該通過編寫不需要注釋的清晰代碼來主動(dòng)避免注釋。
優(yōu)秀的代碼許多特征都會(huì)重復(fù)出現(xiàn),一種技巧的好處可以在代碼質(zhì)量的若干方面得到體現(xiàn)。下面羅列出一些編寫自文檔化代碼的技術(shù)。
使用好的樣式編寫簡單的代碼
樣式極大地影響著代碼的清晰度。考慮周密的版面排布表達(dá)了代碼的結(jié)構(gòu),它使得函數(shù)、循環(huán)和田間語句更加明確。
- 讓“正常”的流程明顯地貫穿你的代碼。也就是說保持結(jié)構(gòu)的一致性,例如,你的if-then-else結(jié)構(gòu)的順序應(yīng)該前后一致(總是將“正常”情況放在“錯(cuò)誤”情況之前,或者反過來)。
- 避免過多地嵌套語句。這些語句會(huì)導(dǎo)致復(fù)雜的代碼,并且需要冗長的解釋說明。人們通常認(rèn)為每個(gè)函數(shù)都應(yīng)該有且僅有一個(gè)出口,這杯成為“單入口單出口”(Single Entry, Single Exit, SESE)代碼。但是,這要求對(duì)于可讀性代碼來說太嚴(yán)格了,并且可能會(huì)造成深層的嵌套。與下面這個(gè)SESE變體相比,我們更加喜歡前面看到的斐波拉契的例子:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
為了一條額外的return語句,我寧愿避免哪個(gè)不必要的嵌套——它使得函數(shù)更難以讀懂。函數(shù)邏輯深處的return語句,其作用值得質(zhì)疑,但頂端簡短的循環(huán)卻極大地提高了函數(shù)的可讀性。
- 要謹(jǐn)慎地優(yōu)化代碼,防止它不再清晰地表達(dá)基礎(chǔ)的算法。除非你已經(jīng)非常明確某段代碼是可接受的程序函數(shù)的一個(gè)瓶頸,否則不要對(duì)代碼進(jìn)行優(yōu)化,即便優(yōu)化了,也要清晰地注釋這段代碼發(fā)生了哪些變化。
選擇有意義的名稱
關(guān)于這一點(diǎn),可以參考閱讀:C/C++ 名正則言順。
分解為原子函數(shù)
- 一個(gè)函數(shù),一種操作。無需編寫非常復(fù)雜的函數(shù),在一個(gè)函數(shù)中,只需進(jìn)行一種操作,選擇一個(gè)毫無歧義的說明這種操作的名稱,既有利于代碼的利用效率,也便于移植和維護(hù)。
- 減少任何出人意料的副作用,不管他們看上去多么無害。
- 保持簡短。短小的函數(shù)易于理解,如果能分解為帶有描述性名稱的小代碼塊,那么即便是一個(gè)復(fù)雜的算法你也可以輕易地弄明白。
選擇有描述型的類型
盡可能使用現(xiàn)有語言的功能來描述約束或行為:
- 如果你要定義一個(gè)永遠(yuǎn)都不變得值,那么就強(qiáng)制將它定義為常量類型(C/C++中使用const);
- 如果一個(gè)變量不應(yīng)包含負(fù)值,那么就使用無符號(hào)類型;
- 使用枚舉emun來描述一組相關(guān)的值;
- 選擇適當(dāng)?shù)念愋汀?/li>
命名常量
看到類似if(count == 76)的代碼時(shí)往往令人困惑,數(shù)字76到底是什么意思?這句話的含義又是什么?這樣的數(shù)字都隱藏了其含義,不利于閱讀和理解,但是使用下面的方法:
const size_t bananas_per_cake = 76; ... if (count == bananas_per_cake) {// make banana cake }- 1
- 2
- 3
- 4
- 5
- 6
就會(huì)清晰很多(也有很多人習(xí)慣使用宏定義的放法#define BANANAS_PER_CAKE 76,但是高效C++的做法里,往往建議使用const來取而代之)。
強(qiáng)調(diào)重要的代碼
突出重要的代碼,把讀者的注意力吸引到正確的地方。編程時(shí)有很多機(jī)會(huì)可以做到這一點(diǎn):
- 在類中按照一定的順序進(jìn)行聲明。公共信息應(yīng)該被放在首位,然后將私有的實(shí)現(xiàn)細(xì)節(jié)放在最后。
- 盡可能隱藏所有不重要的信息。不要以無用的垃圾信息塞滿全局命名空間。
- 不要隱藏重要的代碼。每行只寫一條語句,并保持每條語句都簡單。
- 限制嵌套的條件語句的數(shù)量。如果不加以限制,重要條件的處理就會(huì)被層層嵌套的if語句和括號(hào)隱藏起來。
提供頭文件
在文件的頂部放置一個(gè)注釋塊,以描述文件的內(nèi)容以及該文件所屬的項(xiàng)目(甚至提供代碼創(chuàng)建和編輯人的名單及其郵箱等)。這并不需要花費(fèi)太多精力,但是卻有明顯地效果。當(dāng)有人維護(hù)這個(gè)文件時(shí),他們就能馬上掌握關(guān)于該文件的信息。
這個(gè)頭文件非常重要,大多數(shù)公司處于法律方面的考慮,要求在每個(gè)源文件都必須包含一個(gè)可見的版權(quán)聲明。以cv.h(OpenCV 2.4.10版本的)為例:
/*M/// // // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. // // By downloading, copying, installing or using the software you agree to this license. // If you do not agree to this license, do not download, install, // copy or use the software. // // // License Agreement // For Open Source Computer Vision Library // // Copyright (C) 2000-2008, Intel Corporation, all rights reserved. // Copyright (C) 2009, Willow Garage Inc., all rights reserved. // Third party copyrights are property of their respective owners. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistribution's of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // * Redistribution's in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // * The name of the copyright holders may not be used to endorse or promote products // derived from this software without specific prior written permission. // // This software is provided by the copyright holders and contributors "as is" and // any express or implied warranties, including, but not limited to, the implied // warranties of merchantability and fitness for a particular purpose are disclaimed. // In no event shall the Intel Corporation or contributors be liable for any direct, // indirect, incidental, special, exemplary, or consequential damages // (including, but not limited to, procurement of substitute goods or services; // loss of use, data, or profits; or business interruption) however caused // and on any theory of liability, whether in contract, strict liability, // or tort (including negligence or otherwise) arising in any way out of // the use of this software, even if advised of the possibility of such damage. // //M*/- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
或許你寫不了這么清晰,詳盡的頭文件,但是也應(yīng)該提供一些最基本的描述:
/********************************************************** File: Foo.h* Purpose: Foo class implementation* Notice: (c) 1066 Foo industries. All rights reserved.*********************************************************/- 1
- 2
- 3
- 4
- 5
恰當(dāng)?shù)靥幚礤e(cuò)誤
在最恰當(dāng)?shù)纳舷挛闹刑幚礤e(cuò)誤。如果磁盤I/O有問題,你應(yīng)該在訪問磁盤的代碼中處理這個(gè)問題。處理這個(gè)錯(cuò)誤可能會(huì)引起另一個(gè)更高層的錯(cuò)誤(如無法加載文件異常)。這意味著在程序的每一層上,一個(gè)錯(cuò)誤就是此上下文中的問題是什么的一個(gè)精確描述。自文檔化代碼能幫助讀者了解錯(cuò)誤的起因,錯(cuò)誤意味著什么,以及對(duì)程序在這一點(diǎn)的暗示。
編寫有意義的注釋
前面已經(jīng)講述了如何嘗試使用隱代碼文檔化技術(shù)來避免編寫注釋。不過,在你編寫出所能編寫的最清晰的代碼之后,其余信息仍然需要使用注釋來說明。
只有在你無法以任何其他方式來提高代碼清晰度的情況下,再添加注釋。
最后,總結(jié)一下。關(guān)于寫作技巧的提高,有一個(gè)簡單的原則:讀的書越多,寫得就越好。用批判的眼光閱讀著名作者的作品,可以是你學(xué)會(huì)如何分辨好壞。你會(huì)從中學(xué)到新的技巧和習(xí)慣用法,為你的知識(shí)庫添磚加瓦。類似地,如果你閱讀大量的代碼,那么你將會(huì)成為更出色的程序員。如果你將自己沉浸在優(yōu)秀的代碼中,你很快就會(huì)具備在千里之外就能聞到糟糕代碼的能力。有了這些經(jīng)驗(yàn),你自然而然地會(huì)發(fā)現(xiàn)自己在編寫代碼時(shí)能運(yùn)用好的技巧,當(dāng)你編寫出糟糕代碼時(shí),你會(huì)馬上有所警覺,讓你渾身不自在。
--------------------- 本文來自 yhl_leo 的CSDN 博客 ,全文地址請(qǐng)點(diǎn)擊:https://blog.csdn.net/yhl_leo/article/details/50545047?utm_source=copy
總結(jié)
- 上一篇: linux中断的上半部和下半部
- 下一篇: 深入理解C语言变量和内存——整理篇