何為優(yōu)雅的代碼
????????優(yōu)雅的代碼,至少需要遵循以下幾個原則:
????????遵守規(guī)范
????????優(yōu)雅的代碼,首先讓人看起來就是很整潔的。而這種整潔,則來源于代碼規(guī)范。嚴(yán)格地遵守代碼規(guī)范,是提高且保證代碼質(zhì)量的最有效方法。從個人開發(fā)的角度來看,一份良好的代碼規(guī)范,能夠?qū)Υa整潔起到指導(dǎo)幫助作用。從多人協(xié)作的角度來看,統(tǒng)一的代碼規(guī)范能夠有效減少溝通的阻礙。
????????邏輯清晰
????????代碼是邏輯的產(chǎn)物。編寫代碼時,業(yè)務(wù)相關(guān)的邏輯一定要清晰明確,不能模棱兩可。除此之外,對于所定義的變量、代碼塊、數(shù)據(jù)結(jié)構(gòu)、方法、類、模塊等,也要有邏輯地組織它們。
????????嚴(yán)謹(jǐn)可靠
????????嚴(yán)謹(jǐn)?shù)拇a,才能保證它的可靠性,減少bug的發(fā)生幾率。一份代碼即使嚴(yán)格遵守了代碼規(guī)范,但思考不全面,邏輯不嚴(yán)謹(jǐn),到處都是各種漏洞和bug,也不能稱之為優(yōu)雅。
為什么要寫優(yōu)雅的代碼
????????軟件工程師有一大籮筐的借口來抵制提升代碼質(zhì)量的要求,更不用說還要編寫優(yōu)雅的代碼了。想讓我寫優(yōu)雅的代碼,沒門!
????????1、項目進(jìn)度這么趕,bug這么多,改都改不完,哪有時間寫好代碼?
????????2、代碼寫得好不好無所謂,也無關(guān)緊要,能完成功能,并且不出什么bug就萬事大吉了。
????????3、上一個人留下的代碼就是那么爛,我能怎么辦?我也沒辦法,好無奈啊。
????????4、代碼寫得再好有什么用?公司又不給加薪升職。
????????5、公司那么摳,加班那么嚴(yán)重,領(lǐng)導(dǎo)那么挫,還指望我好好寫代碼?
????????... ...
????????這里給出軟件工程師必須寫優(yōu)雅的代碼的幾點理由。
????????1、代碼首先是寫給人看的,其次才是計算機(jī)能夠運(yùn)行。
????????能完成功能,也就是完成了開發(fā)任務(wù),是應(yīng)該的。但我們的項目是持續(xù)迭代的,以前寫的代碼以后還要去查看和修改,如果每次都只有完成功能的最低要求,日積月累,這個項目所能達(dá)到的質(zhì)量也只會是最低要求,并且這個最低要求還會進(jìn)一步降低。
????????另外,你所編寫的代碼,在你維護(hù)這個項目的期間,你是面對它最長時間的人。寫得好,你看起來會舒服,心情也會更好;寫得爛,惡心也只會惡心到你自己。
????????2、寫好代碼更能省時間。其實比起寫低質(zhì)量的代碼,寫出優(yōu)雅的代碼更能節(jié)省時間。
????????首先,優(yōu)雅的代碼是邏輯清晰的,簡單直觀的,所以在開發(fā)或維護(hù)的時候,讀邏輯清晰的代碼,自然要比讀邏輯混亂的代碼要更容易,由此就可以把更多的精力與時間花在功能開發(fā)上,而不是理清以前邏輯上。
????????其次,編寫代碼時,思維清晰,就可以寫出更嚴(yán)謹(jǐn)?shù)拇a,這樣就能減少bug,也就減少了修復(fù)bug所花費(fèi)的時間。
????????不應(yīng)該把時間都耗費(fèi)在代碼的修復(fù)上,而應(yīng)該更多地用于創(chuàng)造性的工作上。而編寫優(yōu)雅的代碼,正是達(dá)成這一目標(biāo)的有效方法。
????????3、做一個有所追求的軟件工程師,而不是一個得過且過的碼農(nóng)。
????????你的代碼質(zhì)量,應(yīng)該取決于你自己,而不是你的公司、你的領(lǐng)導(dǎo)、產(chǎn)品經(jīng)理、設(shè)計人員,或是項目以前的負(fù)責(zé)人。有追求的你,不應(yīng)該讓他們成為你降低自己要求的理由。你對自己有所追求,對代碼也應(yīng)當(dāng)有所追求。
大牛們的話
????????關(guān)于寫優(yōu)雅的代碼這個話題,很多大牛們發(fā)表了自己的見解,一起來了解一下吧。
????????最好的軟件開發(fā)人員都知道一個秘密:美的東西比丑的東西創(chuàng)建起來更廉價,也更快捷。而構(gòu)建、維護(hù)一個美的軟件系統(tǒng)所花費(fèi)的時間、金錢都要少于丑的系統(tǒng)。美的系統(tǒng)是靈活、易于理解的,構(gòu)建、維護(hù)它們就是一種快樂。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?—— 《敏捷軟件開發(fā)》
????????代碼質(zhì)量與其整潔度成正比。干凈的代碼,既在質(zhì)量上較為可靠,也為后期維護(hù)和升級奠定了良好基礎(chǔ)。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??—— 《代碼整潔之道》
????????在代碼閱讀中說臟話的頻率,是衡量代碼質(zhì)量的唯一標(biāo)準(zhǔn)。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??—— Robert C. Martin,世界級編程大師,設(shè)計模式和敏捷開發(fā)先驅(qū)
????????簡潔的代碼簡單直接。簡潔的代碼如同優(yōu)美的散文。簡潔的代碼從不隱藏設(shè)計者的意圖,充滿了干凈利落的抽象和直接了當(dāng)?shù)目刂普Z句。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?—— Grady Booch,美國Rational軟件工程公司的首席科學(xué)家
????????我喜歡優(yōu)雅和高效的代碼。代碼邏輯應(yīng)該直接了當(dāng),叫缺陷難以隱藏;盡量減少依賴關(guān)系,使之便于維護(hù);依據(jù)某種分層戰(zhàn)略完善錯誤處理代碼。性能調(diào)制最優(yōu),省得引誘別人做沒規(guī)矩的優(yōu)化,搞出一堆混亂來。整潔的代碼只做好一件事。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??—— bjarne stroustrup,C++語言發(fā)明者
????????我可以列出我留意到的簡潔代碼的所有優(yōu)點,但其中有一條是根本性的。簡潔的代碼總是看起來像是某位特別在意它的人寫的。幾乎沒有改進(jìn)的余地,代碼作者幾乎什么都想到了,如果你企圖改進(jìn)他,總會回到原點,贊嘆某人留給你的代碼 —— 全心投入的某人留給你的代碼。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?——《修改代碼的藝術(shù)》
????????如果每個例程都讓你感到深合己意,那就是整潔代碼。如果代碼讓編程語言看起來像是專為解決那個問題而存在,就可以稱之為漂亮的代碼。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??—— Ward Cunningham,wiki發(fā)明者
如何編寫優(yōu)雅的代碼
????????1、盡量消除硬編碼。
????????什么是硬編碼,就是說在代碼里面,你用的一些變量是寫死的。在代碼中需要硬編碼時,我們要想一想,這個硬編碼的值在某些情況下會改變嗎,為什么是這個值而不是其他值,確定需要這個硬編碼的值嗎,等等。當(dāng)我們思考這些問題后,你會發(fā)現(xiàn),絕大部分硬編碼可以轉(zhuǎn)換成常量、宏定義、程序配置等,或者根本不需要。
????????2、代碼不是越短越好。
????????減少代碼行數(shù)是一個好目標(biāo),但是減少閱讀代碼的時間是一個更好的目標(biāo)。
assert((!(bucket = findBucket(key))) || !bucket.isOccupied());
????????上面的代碼只有一行,看似很簡潔,但不便于閱讀,最好修改為如下代碼。
bucket = findBucket(key);
if(bucket != null && !bucket.isOccupied())
????????3、注釋不是越多越好。
????????(1)不要為那些從代碼本身就能快速推斷的事實寫注釋。注釋一定是表達(dá)代碼之外的東西,代碼可以包含的內(nèi)容,注釋中盡量不要出現(xiàn)。比如,下面示例代碼中的注釋就是多余的。
class CStudent
{
public:
// Constructor
CStudent();
// Set the name member to a new value
void setName(const string &strName);
};
????????(2)不要給不好的名字寫注釋,而應(yīng)該把名字改好。
// Releases the handle for this key.This doesn't modify the actual registry.
void deleteRegistry(RegistryKey key)
????????上面代碼中的注釋和函數(shù)名不是很匹配,應(yīng)當(dāng)從源頭上修改函數(shù)名,而不是添加注釋來費(fèi)力地解釋。
void releaseRegistryHandle(registryKey key);
????????(3)不要總想著用注釋來解決看不懂代碼的問題。如果你的代碼不易理解,那就應(yīng)該去改進(jìn)它,讓它能夠“自解釋”(把命名當(dāng)做一種注釋的方式,讓它承載更多的信息),而不是依賴于大段的注釋。
????????4、適當(dāng)?shù)刈⑨尅?/span>
????????(1)給重要的語句、解決的bug、關(guān)鍵的邏輯、疑難問題等添加注釋。
????????(2)給常量、宏定義添加注釋。
????????(3)給代碼中的瑕疵添加注釋。
????????(4)站在讀者的角度寫注釋。請注釋意圖(why),而不要注釋實現(xiàn)(how),大家都會看代碼。
????????5、根據(jù)邏輯組織代碼。
????????(1)聲明成員變量的時候,通常應(yīng)當(dāng)把某個種類,或者某個功能點相關(guān)的聲明放在一起,并且不同種類或功能點的聲明之間應(yīng)當(dāng)留有空行。
????????(2)聲明方法的時候,應(yīng)當(dāng)把相關(guān)聯(lián)的方法放在一起,并用空行隔開,而不是按名字排序,或者從上到下一直堆砌。比如:定義了A(), B(), C()三個方法,其中A()調(diào)用了C(),那就應(yīng)該把C()放在A()之下,而把B()放在這兩個方法的上面或下面,而不是中間。
????????(3)在方法的內(nèi)部,應(yīng)當(dāng)根據(jù)具體的業(yè)務(wù)用空行隔開不同的邏輯,這樣不但使代碼顯得有段落感,也有助于對代碼的整體理解。
????????(4)在定義類時,相關(guān)聯(lián)的類應(yīng)互相靠近。同一個庫/包里的類應(yīng)該是共同完成某個模塊或功能的,即庫/包內(nèi)的代碼應(yīng)是高內(nèi)聚的。
????????6、命名的原則:正確、準(zhǔn)確、直觀、簡潔。
????????(1)正確是指:變量名能夠表現(xiàn)它的屬性,方法名能夠表現(xiàn)它的行為,類名能夠表現(xiàn)它的職責(zé),庫名/包名能夠表現(xiàn)它的功能。
????????(2)在做到正確的前提下,再去追求準(zhǔn)確。準(zhǔn)確是指它的名字是正確并精確的,比如:用手機(jī)號是否為空去判斷一個用戶是否綁定了手機(jī)號,方法名isPhoneBounded()就要比isPhoneNotEmpty()要貼切些,盡管它里面的實現(xiàn)是判斷手機(jī)號是否為空。
????????(3)直觀是排在簡潔前面的,名字長點沒關(guān)系,只要直觀就可以。
????????(4)在同樣直觀的前提下,如果有更簡潔的命名,再選用更簡潔的命名。命名可以使用縮寫,但應(yīng)該是大家約定俗成的縮寫。比如:把message縮寫成msg是可以接受的,但把notification縮寫為noti或notif,是不太恰當(dāng)?shù)摹?/p>
????????7、不要把所有變量都定義在開頭。
????????(1)把所有變量定義在開頭是C語言的風(fēng)格,面向?qū)ο笳Z言習(xí)慣將變量定義在它開始使用的地方。也就是,什么時候開始使用,什么時候定義。
????????(2)用到的時候再定義,既可以縮小變量的作用域,也更符合人的邏輯思維習(xí)慣。
????????8、提高代碼可讀性。
????????(1)通過提前返回減少嵌套。
if (a) {
if (b) {
if (c) {
// ...
return true;
}
}
}
????????嵌套太多,不利于閱讀和理解,可以修改為如下方式。
if (!a) {
return false;
}
if (!b) {
return false;
}
if (!c) {
return false;
}
// ...
return true;
????????(2)適當(dāng)?shù)乜偨Y(jié)變量。
if (A == B) {
}
if (A != B) {
}
????????對A == B表達(dá)式進(jìn)行總結(jié),便于后面復(fù)用。
bool bRet = A == B;
if (bRet ) {
}
if (!bRet ) {
}
????????(3)減少控制流里使用的變量。
boolean done = false;
while (/* condition */ && !done) {
if (...) {
done = true;
continue;
}
}
????????事實上,上面的代碼可以不用done變量。
while (/* condition */) {
if (...) {
break;
}
}
????????9、提高代碼可維護(hù)性。
????????(1)有互斥鎖時,或者有資源創(chuàng)建時,除了對入?yún)⒌臋z查可以直接return外,其他地方盡量不要中途return,只在方法的最后進(jìn)行return。
mutex.lock();
if (!A) {
mutex.unlock();
return -1;
}
if (!B) {
mutex.unlock();
return -2;
}
mutex.unlock();
return 0;
????????上面的代碼,在每個中途都可能return,此時需要記得釋放互斥鎖,很容易漏掉,從而導(dǎo)致程序異常。更好的方法是封裝自動釋放鎖的輔助類,或者修改程序結(jié)構(gòu),不要中途return。
????????(2)代碼中有多個資源需要動態(tài)創(chuàng)建和釋放時,可采用do while結(jié)構(gòu),既便于理解,也不易引入錯誤。
char *pBuf1 = NULL;
if (A) {
pBuf1 = new char[256];
}
if (B) {
delete[] pBuf1;
return -1;
}
char *pBuf2 = NULL;
if (C) {
pBuf2 = new char[1024];
}
if (D) {
delete[] pBuf1;
delete[] pBuf2;
return -2;
}
// ...
delete[] pBuf1;
delete[] pBuf2;
return 0;
????????上述代碼,在每個return的地方,都需要記得釋放內(nèi)存,不利于后期維護(hù),很容易犯錯。改為下面的do while結(jié)構(gòu)后,代碼變得更為簡潔,也更易于維護(hù)了。
int nRet = 0;
char *pBuf1 = NULL;
char *pBuf2 = NULL;
do
{
if (A) {
pBuf1 = new char[256];
}
if (B) {
nRet = -1;
break;
}
if (C) {
pBuf2 = new char[1024];
}
if (D) {
nRet = -2;
break;
}
} while (false);
// ...
delete[] pBuf1;
delete[] pBuf2;
return nRet;
????????10、一個函數(shù)只做一件事。
????????(1)編寫函數(shù)時,記住兩條規(guī)則。第一條規(guī)則是要短小,第二條規(guī)則是還要更短小。
????????(2)更短小的函數(shù),更易于閱讀和理解。如果一個函數(shù)的代碼過多,很可能是由于你沒有對這個函數(shù)的功能進(jìn)行深入思考和進(jìn)一步分解。函數(shù)越大,滋生bug的幾率越大,后期維護(hù)的成本也越大。
????????11、封裝。
????????(1)時時刻刻將封裝牢記在心:將不需要使用者知曉的細(xì)節(jié)全部隱藏起來,只暴露最少的接口給使用者。
????????(2)在各個不同的維度和層次使用封裝,設(shè)計函數(shù)、類、包、庫、模塊、系統(tǒng)、架構(gòu)時,都要仔細(xì)考慮如何進(jìn)行封裝。
????????(3)封裝良好的代碼,一定是高內(nèi)聚、低耦合的,便于理解、擴(kuò)展和維護(hù)。
????????12、全面思考。
????????(1)對一個問題思考得越全面,編寫出的代碼就會越嚴(yán)謹(jǐn)。
????????(2)多反問自己:這個接口被調(diào)用多次怎么辦?這個接口不被調(diào)用,其他地方會崩潰嗎?多線程使用這個接口會有問題嗎?為什么這里要加鎖,那里又不加鎖?寫了if,沒寫else,但else真的不用處理嗎?… …
總結(jié)
-
平均來說,一次編寫的代碼會被閱讀十次,所以盡力保持代碼優(yōu)雅是有意義的。當(dāng)養(yǎng)成習(xí)慣后,你會發(fā)現(xiàn)基本不需要花什么力氣,更優(yōu)雅、更具維護(hù)性的代碼很快就會產(chǎn)生收益。
-
優(yōu)雅的代碼有助于理解開發(fā)語言、模式和架構(gòu),也有利于提升開發(fā)水平。
-
史蒂芬·金在《關(guān)于寫作》中說,想要成為優(yōu)秀的作家需要大量的閱讀和大量的寫作。當(dāng)人們問Henny Youngman如何能做到在卡內(nèi)基音樂廳演奏的時候,他的答案是:“練習(xí),練習(xí),再練習(xí)?!睂τ谲浖_發(fā)也是如此:閱讀他人優(yōu)雅的代碼,編寫代碼,不斷練習(xí)。但很多人忽略了第一步和第三步,總是在不停地編寫代碼。實際上,第一步和第三步更為重要,它反應(yīng)了我們借鑒、學(xué)習(xí)和思考的過程。
-
墨菲定律中有一條:凡是你認(rèn)為可能會出錯的,它一定會出錯。這條定律在軟件開發(fā)中非常常見,所以寫代碼時一定要嚴(yán)謹(jǐn),而不是想著先把功能加好,后面再慢慢去優(yōu)化重構(gòu)。記住勒布朗法則:Later equals never(稍后等于永不)。
-
編寫代碼,如同寫一篇文章。文章的段落劃分,如同代碼的架構(gòu)組織。文章的標(biāo)點符號,如同代碼中的空格、空行。文章的措辭修飾,如同代碼中的命名。文章的主線情節(jié),如同代碼的封裝。好的文章,段落劃分、標(biāo)點符號、措辭修飾、主線情節(jié),每一樣都是精雕細(xì)琢。優(yōu)雅的代碼,亦是如此。
推薦書籍
? ? ?
文章來源:http://www.zghlxwxcb.cn/news/detail-457202.html
?? ?
文章來源地址http://www.zghlxwxcb.cn/news/detail-457202.html
到了這里,關(guān)于軟件工程師,要么不寫代碼,要么就寫優(yōu)雅的代碼的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!