如何提高代碼品味

如何提高代碼品味

一家之言,可以在評論里探討

寫代碼雖然大多數(shù)時候是個體力活,但不可否認,也需要一點品位。我曾經(jīng)覺得代碼質(zhì)量很重要,后來寫業(yè)務(wù)寫多了,又覺得如果連代碼正確都做不到,又談何代碼質(zhì)量。后來我又醒悟了,這世上很難有 bug free 的代碼,當(dāng)出現(xiàn) bug
的時候,好代碼比爛代碼會好改很多。我們今天就討論下什么是好代碼,畢竟一個不知道什么樣的代碼是好代碼的人是不可能如有神助寫出好代碼的,寫代碼可以搜索復(fù)制黏貼三板斧,寫好代碼卻是必須刻意練習(xí)的。

什么是寫代碼

我覺得寫代碼分為兩個部分:

  • 結(jié)構(gòu)設(shè)計,包括模塊劃分、模塊交互、接口設(shè)計
  • 功能實現(xiàn),涉及具體的語言特性以及代碼風(fēng)格

結(jié)構(gòu)設(shè)計

所謂的結(jié)構(gòu)設(shè)計不是說一定要畫個架構(gòu)圖,寫個系分文檔什么的,結(jié)構(gòu)設(shè)計和功能實現(xiàn)其實螺旋貫穿在整個寫代碼的過程中。當(dāng)我們準(zhǔn)備完成一個需求的時候,會把需求分成幾個功能,這些功能如果互相獨立,便不涉及交互,否則他們之間就需要溝通,可能是直接調(diào)用,可能是發(fā)送消息,可能是監(jiān)聽變化,可能是輪詢結(jié)果等等。分了功能之后,要實現(xiàn)其中某個功能,又要遞歸的執(zhí)行一遍上述過程,直到寫下一行行代碼。有同學(xué)可能覺得這種自頂向下的過程太宏觀了,前期太費時間,什么模塊什么交互,我就挑個功能一把梭,代碼先寫起來。這當(dāng)然也可以,而且大多數(shù)人都是這么做的。但這其實也包含結(jié)構(gòu)設(shè)計,你準(zhǔn)備率先實現(xiàn)的那個功能,潛意識里你已經(jīng)把它從整個系統(tǒng)中分離出來了,只是系統(tǒng)的其他部分暫時先不管而已。模塊劃分是個說爛的話題,但它又真的是軟件工程的精髓,它的意義在于,人管理復(fù)雜度的能力是有限的,當(dāng)一大坨代碼懟在一起的時候,哪怕代碼質(zhì)量再高,注釋再詳盡,也會引起生理上的不適。這種不適最容易發(fā)生在當(dāng)你要修改一個小功能,找了半天代碼找不到的時候。劃分了之后,哪怕是好幾坨爛代碼,但你改動的時候只改其中一個文件,其他代碼也是眼不見心不煩。

那模塊如何劃分?我們可以說出一些普適的原則,譬如高內(nèi)聚低耦合、單一職責(zé)原則、開閉原則等等,但這些東西說起來感覺很套路很不真誠,讓人覺得無從下手。我個人覺得有兩條很重要的原則:

  • 開發(fā)的過程中時刻反思自己的代碼結(jié)構(gòu)
  • 認真命名

關(guān)于反思代碼結(jié)構(gòu),最重要的當(dāng)然是自己的思考,不要迷信別人給你做的設(shè)計,也不要迷信自己當(dāng)初的設(shè)計,我大概列舉幾種情況:

  • 一開始分了兩個模塊,寫著寫著發(fā)現(xiàn)其中一個模塊的體積很大,那就看看能不能再繼續(xù)分
  • 一開始分了四五個模塊,后來寫著寫著發(fā)現(xiàn)其中兩個模塊交互巨頻繁,他們必須一起配合才能實現(xiàn)一個完整的功能,那就把他們合在一起(高內(nèi)聚)
  • 在迭代的過程中發(fā)現(xiàn)兩個模塊雖然相互獨立,但交互邏輯寫得很死,依賴關(guān)系很直接,修改了一個,另一個也必須改,那就修改他們的依賴和交互,盡量做到互不影響(松耦合)
  • 在迭代過程中發(fā)現(xiàn)兩個模塊的某部分是可以共用的,那就抽出來(DRY)
  • 在迭代過程中發(fā)現(xiàn)某個模塊的一段代碼變更很頻繁,那就單獨把這部分抽出來(封裝變化)
  • 。。。

這些情況當(dāng)然列舉不完,團隊中其實可以定一些硬性指標(biāo)來輔助模塊劃分,譬如一個文件最多 300 行,一個函數(shù)最多 70 行什么的,放在 Lint 規(guī)則里。我們有很多約定俗成的“潛規(guī)則”其實都有它背后的邏輯,譬如以前天天說的 MVC 和 MVVM 兩個模式,他們的最主要區(qū)別不在于模塊劃分,而是模塊間的交互,在劃分方面它們都致力于讓 UI 和邏輯分開,為什么呢?因為 UI is cheap,UI 是隔三差五會被設(shè)計師推翻再來一套的,但邏輯和數(shù)據(jù),相對會穩(wěn)定一點,所以把他們分開,UI 迭代的時候涉及的改動就比較小。那為什么前端的 React/Vue
這些近年大火的框架,又提倡所謂的組件(Component),貌似 是要把 UI 和邏輯搞在一起呢?其實不是的,組件是一個小粒度的模塊,它相對獨立,具有很高的內(nèi)聚性,組件中的“邏輯”更多的應(yīng)該是 UI 相關(guān)的交互邏輯,而不是那些比較底層的邏輯,我們還是應(yīng)該把相對穩(wěn)定的邏輯抽出來放到更下層。

關(guān)于命名,很多同學(xué)可能不在意,覺得代碼能跑就行了,取名字有什么關(guān)系,看不懂我加注釋嘛。這是非常不好的習(xí)慣,因為命名的過程中其實就是在概括你這段代碼,如果你的某個函數(shù)名叫 xxxAndxxx
那這個函數(shù)就應(yīng)該被拆成兩個函數(shù),它明顯違反了單一職責(zé)原則。大到業(yè)務(wù)模塊小到輔助函數(shù),只要你覺得不好命名,那就是一個信號,說明這段代碼做的事情太多太雜,以至于你無法用幾個單詞概況出來。

功能實現(xiàn)

現(xiàn)在我們具體聊聊代碼實現(xiàn)的時候怎么體現(xiàn)代碼品位,我覺得主要可以從三點著手提高:

  • 精通你所用的編程語言
  • 提高自己的邏輯能力
  • 注重代碼風(fēng)格

有一個廣為人知的觀點,編程思路最重要,語言只是工具。乍一聽,編程語言似乎無足輕重。如果只是一錘子買賣,寫段代碼實現(xiàn)個功能,寫完離手,再不相干,那語言當(dāng)然不重要。我相信大多數(shù)碼農(nóng)都可以很快上手一個新語言,因為要實現(xiàn)一個功能可能只需要一些通用的核心特性,對 C 系語言來說,知道函數(shù)/分支語句/循環(huán)語句/字符串/數(shù)組/散列表這些東西的使用就足夠開發(fā)日常需求了,而這些特性說實話在 C
系語言中都大同小異。但如果要寫好代碼,就得向著精通這門語言努力。有些功能你寫一堆蹩腳代碼可能實現(xiàn)得馬馬虎虎,但用了某個特性,幾行短小精悍的代碼就解決了。我很排斥一個觀點是,為了讓團隊的所有人都能看懂,鼓勵只使用語言的基本特性,一些高級的或者不太常用的特性不準(zhǔn)用,用了就是炫技。舉個極端點的例子,有人覺得 if else 比三元表達式更可讀,就鼓勵只使用 if else。這樣的“可讀”在我看來只是迎合平庸的碼農(nóng)。有些語言特性確實有利有弊,有的甚至只有弊(JS
中就很常見),那盡量不用,或者根據(jù)實際場景做取舍。我們?nèi)∩岬臉?biāo)準(zhǔn)是“場景”,而不應(yīng)該是人。什么叫合適的場景呢,還是拿三元表達式舉例:

const data1 = a > b ? 100 : 200;
const data2 = a > b ? 300 : 400;

我看到過有同學(xué)這樣用,這段代碼雖然只有兩行,但 a > b 這個條件要判斷兩次,性能我們且不論(a > b 只是個示例,實際的條件可能更復(fù)雜),至少已經(jīng)重復(fù)了,寫的時候?qū)憙纱危x的人也要看兩次,不如就

let data1 = 200;
let data2 = 400;
if (a > b) {
    data1 = 100;
    data2 = 300;
}

或者把 a > b 這個 condition 提前計算好,避免計算兩次(這種優(yōu)化不是針對性能,因為有些語言編譯器在編譯期會做類似這種優(yōu)化,主要還是可讀性):

const condition = a > b;
const data1 = condition ? 100 : 200;
const data2 = condition ? 300 : 400;

代碼要簡潔,但不是簡短(代碼行數(shù)少),簡潔的意思是邏輯清晰,沒有冗余信息。還有一個場景是我有看過同學(xué)用嵌套的三元表達式來替代多個 if else 的操作,那真的是可讀性很差了,不要這樣。

這方面也有一些具體的技巧,譬如我個人很喜歡用散列表去代替分支語句:

// if (key === 'x') return 'xxx';
// if (key === 'y') return 'yyy';
// if (key === 'z') return func;

const xxxMap = {
    x: 'xxx',
    y: 'yyy',
    z: func
};
return xxxMap[key];

這種做法好像有個名字叫“表驅(qū)動設(shè)計”,這樣做有很多好處,代碼簡潔是一點,這個 xxxMap 其實是一張配置表,以后可以抽出去放到單獨的地方,甚至放到服務(wù)端去配置,就可以很容易實現(xiàn)一些動態(tài)化需求。更延伸開去講,能配置化的東西盡量都配置化,方便以后擴展功能。

再舉個 reduce 的例子,如何根據(jù)場景選擇:

const reservedWords = ['initialState', 'state', 'effects', 'actions', 'updates', 'mutations'];

// 這里其實用 reduce 會好看些:
// store => reservedWords.reduce((acc, x) => acc || store.hasOwnProperty(x), false)
// 但為了能提前返回,性能稍微好一點,還是用了 for
const isSingleStore = (store) => {
    for (let i in reservedWords) {
        if (store.hasOwnProperty(reservedWords[i])) return true;
    }
    return false;
};

說了語言重要,那思路呢?當(dāng)然也重要。我們?nèi)粘I钪校壿嬊逦娜巳詢烧Z就直擊要害,邏輯混亂的人兜兜轉(zhuǎn)轉(zhuǎn)還是云里霧里。代碼也一樣,有些糟糕的代碼是糟糕在啰嗦,一個判斷能搞定的事情它可能要變著法判斷兩三次。這種呢,就是本身思路不簡潔,寫出來的代碼自然也簡潔不了,只能努力提高自己的邏輯能力。

剩下的代碼風(fēng)格,也非常重要。長得好看的人是有特權(quán)的,長得好看的代碼自然也有。多看看官方或者大廠的 Style Guide,平常多注意空格換行縮進命名風(fēng)格等等,裝個優(yōu)秀的格式化插件也好。

差不多就先這樣吧,品味不見得有好壞,但有高低,共勉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,197評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,415評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,104評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,884評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,647評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,130評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,208評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,366評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,887評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,737評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,478評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,174評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,586評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,827評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,608評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,914評論 2 372

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,630評論 25 708
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 12,774評論 2 59
  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 16,000評論 3 119
  • 【朋朋日歷】 2018 年 9 月 17 日 星期一 農(nóng)歷 八月初八 戊戌年 辛酉月 壬子日 **********...
    劉書朋閱讀 172評論 0 0
  • 尤記得很小的時候,周圍的大人老是逗我,說我是橋底下?lián)斓模职謰寢尣幌矚g你才不把你帶身邊。我那時候說:你們都不愛我,...
    縮沙蜜閱讀 298評論 0 1