這些代碼,差點(diǎn)把我氣出內(nèi)傷
大家好,我是魚皮,一個(gè)正在爛代碼的泥潭里面摸爬滾打的程序員。
先問大家一個(gè)小問題:你覺得看別人代碼累,還是自己寫代碼累?
我相信有很多朋友會(huì)說,當(dāng)然是自己寫代碼累了,要思考邏輯、要?jiǎng)邮智面I盤,身心俱疲啊;但是,如果你需要經(jīng)常閱讀別人的代碼,尤其是爛代碼,答案就不一定了。
因?yàn)樽约簩懘a,邏輯是自己來梳理的、代碼自己是熟悉的;但是看別人的代碼,你就要去理解別人的代碼邏輯,再加上爛代碼的加持,可能很簡(jiǎn)單的邏輯,你都得看半天才能懂。所以很多大佬在面對(duì)前人的 “屎山” 項(xiàng)目時(shí),寧愿自己重寫,也不去讀爛代碼。
舉個(gè)夸張的例子:讓你實(shí)現(xiàn) 1 + 1 的求和
自己寫:
let?sum?=?1?+?1;
某爛代碼可能是:
let?a?=?{};
let?b?=?{};
a.toString?=?function()?{
??return?1;
};
b.valueOf?=?function()?{
??return?"1";
};
let?sum?=?a?+?b;
把簡(jiǎn)單的邏輯搞復(fù)雜,便是絕大多數(shù)程序員的拿手好戲(當(dāng)然也包括我)。
因?yàn)槲覐拇髮W(xué)就開始帶團(tuán)隊(duì)做項(xiàng)目了嘛,所以經(jīng)常會(huì)審查團(tuán)隊(duì)同學(xué)的代碼,做好二次校驗(yàn)。包括現(xiàn)在雖然開公司了,前端 / 后端同學(xué)的代碼,也都會(huì)在我這過一遍才會(huì)發(fā)布上線。
總之算是看了很多代碼,其中有一些真的是讓我哭笑不得。下面給大家分享一些代碼片段出來,希望大家 不要學(xué)習(xí) 。
1、過于抽象的命名
還記得咱們剛學(xué)編程的時(shí)候,變量的命名都是用的 abcdefg。
自學(xué)時(shí)這么寫完全沒問題,但是在實(shí)際項(xiàng)目中,如果還用過于抽象的命名,那就不太合適了。
比如下面這段,大家能看懂是什么意思么?
const?[l,?setL]?=?useState<boolean>(false);
const?[d,?setD]?=?useState<any>();
但如果我稍微完善下命名呢:
const?[loading,?setLoading]?=?useState<boolean>(false);
const?[data,?setData]?=?useState<any>();
很多同學(xué)應(yīng)該立刻能看懂了,一個(gè)是 “加載中” 的變量,一個(gè)是 “存儲(chǔ)數(shù)據(jù)” 的變量。
最好的代碼應(yīng)該是不用寫注釋的,因?yàn)?代碼即注釋 。如果你能把命名做到 “見名知義”,看代碼的人會(huì)極度舒適。
2、有深度的代碼
比如下面這段:
if?(condition1)?{
??//?邏輯?A
??if?(condition2)?{
????//?邏輯?B
????if?(condition3)?{
??????//?邏輯?C
??????if?(condition4)?{
????????//?邏輯?D
??????}
????}
??}
}
這里的深度有 2 重含義,一重是字面意思:代碼一層嵌一層、深不見底;另外一重是指真的 “很有深度” —— 指讓人看不懂。
閱讀這段代碼的感覺就像是你在一座巨大的迷宮里,每次轉(zhuǎn)彎都要判斷下是左還是右,最后你只會(huì)迷失方向。
如何改進(jìn)呢?
最簡(jiǎn)單直接的方法就是使用早返回策略(early return):
if?(!condition1)?
?//?邏輯?A
?return;
if?(!condition2)
??//?邏輯?B
?return;
if?(!condition3)
??//?邏輯?C
?return;
if?(!condition4)
??//?邏輯?D
?return;
這樣,你的代碼就清晰了很多,閱讀這種代碼的感覺就像是走在了一條直路上,前方的路一目了然。
當(dāng)然,還可以將一些邏輯抽象成獨(dú)立函數(shù)來簡(jiǎn)化代碼,或者使用設(shè)計(jì)模式來優(yōu)化。
怎么判斷一段代碼是否過于復(fù)雜、應(yīng)該優(yōu)化了呢?這里提到一個(gè)概念: 圈復(fù)雜度 ,這是一種量化代碼復(fù)雜程度的概念。通常你代碼中的 if else 分支越多,圈復(fù)雜度就越高,代碼就越復(fù)雜。
企業(yè)中一般建議圈復(fù)雜度不要超過 10 - 15,我個(gè)人的編碼習(xí)慣是一般不會(huì)在代碼中出現(xiàn) 3 層以上的嵌套(除非必要)。
3、冗余代碼
這是我遇到最多的問題!分為 3 種情況:
能用一行代碼搞定,偏偏要寫 10 行
能用一個(gè)變量或函數(shù)搞定,偏偏每次都是復(fù)制粘貼重復(fù)寫
沒有用到的代碼,又不舍得刪除
舉個(gè)例子,下面這段前端代碼,大家覺得有什么問題:
這是我們魚聰明 AI 前端開發(fā)過程的真實(shí)代碼
<Spin spinning={!(currDownloadUrl || originPictureUrl || pictureUrl)}> ? {type === DRAW_APP ? ( ? ? drawImg(image) ? ) : ( ? ? drawImg(currDownloadUrl || originPictureUrl || pictureUrl) ? )} </Spin>
第一眼看到這段代碼時(shí),我就發(fā)現(xiàn)了,判斷 spinning(旋轉(zhuǎn))的代碼邏輯比較復(fù)雜,包含了兩個(gè) ||
邏輯。而下面的 drawImg
函數(shù)的參數(shù)中,又包含了這段一模一樣的邏輯。這段判斷,其實(shí)就是冗余代碼,完全沒必要寫兩遍!
我調(diào)整過后的代碼如下:
//?要展示的圖片地址
const?showPictureUrl?=?currDownloadUrl?||?originPictureUrl?||?pictureUrl;
直接定義一個(gè)通用變量,寫上清晰的注釋,其他地方要使用時(shí)就無需關(guān)注內(nèi)部判斷邏輯,看注釋就行了。
這就是所謂的 DRY 原則(Don't Repeat Yourself) ,盡量避免代碼冗余。如果你在多處寫下相同的代碼,那么當(dāng)需要修改這段代碼時(shí),你就需要在所有這些地方都修改,漏一個(gè)地方就是一個(gè) Bug。
還有,關(guān)于項(xiàng)目中沒有用到的函數(shù)和變量,如果你近期沒有使用計(jì)劃,不妨就刪除或者注釋掉,別舍不得。利用好 Git 版本控制系統(tǒng),只要你的代碼提交信息寫得好,要用到被刪掉的歷史代碼時(shí),去提交記錄里找即可。
注意,如果是學(xué)習(xí)時(shí)寫代碼,多保留一些也是合理的。但企業(yè)項(xiàng)目中,項(xiàng)目代碼精簡(jiǎn)凝練些會(huì)更好。
這里打個(gè)比方,寫代碼就像是我在公司里堆東西,一開始總覺得多一點(diǎn)沒關(guān)系,反正有空間。但是,冗余代碼就像是雜物,會(huì)越堆越多,遲早有一天,會(huì)影響到你,就像我們公司現(xiàn)在一樣(右邊有一堆雜物):

大概就分享到這里,請(qǐng)大家在評(píng)論區(qū) @ 出你寫爛代碼的同事吧 ?? ~
歡迎學(xué)編程的朋友們加入魚皮的編程學(xué)習(xí)圈子(yupi.icu) ,和上萬名學(xué)編程的同學(xué)共享知識(shí)、交流進(jìn)步,學(xué)習(xí)原創(chuàng)項(xiàng)目并享有答疑指導(dǎo)服務(wù)。