年薪十萬與年薪百萬程序員寫的代碼的區(qū)別
導(dǎo)讀:編程是一門創(chuàng)造性的工作,是一門藝術(shù)。我們每天與代碼打交道,為什么普通碼農(nóng)辛苦一年只拿十萬,而高級(jí)架構(gòu)師年薪百萬。最主要的就是我們敲出來的代碼有差別,差別在意大部分碼農(nóng)敲出來壞的代碼,而高級(jí)架構(gòu)師能敲出優(yōu)雅的好的代碼。我們每天都會(huì)敲代碼,但當(dāng)被問道什么是好的優(yōu)雅的代碼時(shí),大家可能會(huì)先愣一下,然后給出的回答要么比較空泛,要么比較散,沒辦法簡(jiǎn)單明了地概括出來。顯然,這個(gè)問題并沒有唯一的標(biāo)準(zhǔn)答案,誰都可以談?wù)撟约旱睦斫?。要成為合格的架?gòu)師最基本的要求是能寫好的優(yōu)雅的代碼,所以必須要知道什么是優(yōu)雅代碼。這篇文章我來分享一下阿里系高級(jí)架構(gòu)師對(duì)于好的優(yōu)雅代碼的理解。

一句話概括
衡量代碼質(zhì)量的唯一有效標(biāo)準(zhǔn):WTF/min —— Robert C. Martin

Martin(Bob大叔)曾在《代碼整潔之道》一書中說:當(dāng)你的代碼在做 Code Review 時(shí),審查者要是憤怒地吼道:“What the fuck, is this shit?”、“Dude, What the fuck!”等言辭激烈的詞語(yǔ),那說明你寫的代碼是 Bad Code,如果審查者只是漫不經(jīng)心的吐出幾個(gè):“What the fuck?”,那說明你寫的是 Good Code。
衡量代碼質(zhì)量的唯一標(biāo)準(zhǔn)就是每分鐘罵出“WTF”的頻率。
我敢打賭每個(gè)人都遇到過這樣的情況:過幾周或者幾個(gè)月之后,再看到自己寫的代碼,感覺一團(tuán)糟,不禁懷疑人生。
我們自己寫的代碼,一段時(shí)間后自己看尚且如此,更別提拿給別人看了。
一、好的優(yōu)雅的代碼
我們?nèi)绾蝸硇稳莺玫膬?yōu)雅的代碼?好的優(yōu)雅的代碼一定具備以下特征:
- 精簡(jiǎn)代碼,可讀性高
- 邏輯清晰
- 高內(nèi)聚,低耦合
- OOP三大特征(封裝、繼承、多態(tài))
1、精簡(jiǎn)代碼,可讀性高
任何一個(gè)傻瓜都能寫出計(jì)算機(jī)可以理解的代碼。唯有寫出人類容易理解的代碼,才是優(yōu)秀的程序員。—— Martin Fowler
assert((!(bucket = findBucket(key))) || !bucket.isOccupied());
上面這行代碼雖然比較短,但是難以閱讀。為了更好地閱讀,我們做如下修改:
- bucket = findBucket(key);if(bucket != null){
- assert(!bucket.isOccupied());}
減少代碼行數(shù)是一個(gè)好目標(biāo),但是讓閱讀代碼的事件最小化是個(gè)更好的目標(biāo)。
但是這些詞沒有任何指導(dǎo)意義,我準(zhǔn)備從最基本的概念入手。
所以,談到好代碼,首先跳入自己腦子里的一個(gè)詞就是:精簡(jiǎn)。
好的代碼一定是精簡(jiǎn)的,給閱讀的人一種輕松愉快感覺。
2、邏輯清晰
對(duì)代碼的邏輯層次要有感覺。
比如大體上,一個(gè)程序會(huì)分三個(gè)層次:界面層,邏輯層,數(shù)據(jù)層。簡(jiǎn)化后一般也有兩個(gè)層次:界面和邏輯層。
邏輯層是去掉外表的,內(nèi)在的,實(shí)質(zhì)的東西。一般來說,就是表現(xiàn)為對(duì)數(shù)據(jù)的一組操作。
而界面層,是關(guān)注程序應(yīng)該如何和用戶溝通的。比如可視的視窗,圖表,控件等。它是內(nèi)部邏輯的呈現(xiàn),也是用戶和內(nèi)部邏輯溝通的橋梁。
區(qū)分這兩個(gè)層次的好處,一個(gè)是這兩個(gè)層次所注重的核心內(nèi)容有所不同,用到的技巧或者指導(dǎo)方法有所差別。第二點(diǎn)是,可以將問題解構(gòu)和局部化,減輕開發(fā)難度。第三點(diǎn),有助分開來修改內(nèi)容,比如界面層挪動(dòng)一下,改變一下形式,并不需要修改邏輯層的;而邏輯層改進(jìn)一下算法,也不會(huì)影響界面層的代碼。
對(duì)代碼的邏輯層次有感覺,以上的要求只是很基本的,編寫代碼要時(shí)時(shí)刻刻對(duì)當(dāng)前代碼所代表的邏輯層次要有“感覺”,要能意識(shí)到這段代碼和上一段代碼是否在某種標(biāo)準(zhǔn)下,處在同一個(gè)層次。比較經(jīng)典的范例如:互聯(lián)網(wǎng)的7層協(xié)議,還有操作系統(tǒng)的層次分部等。編寫代碼要善于歸納這些層次,才能建構(gòu)一個(gè)優(yōu)美的結(jié)構(gòu)。
3、高內(nèi)聚低耦合
高內(nèi)聚低耦合幾乎是每個(gè)程序員員都會(huì)掛在嘴邊的,但這個(gè)詞太過于寬泛,太過于正確,所以聰明的編程人員們提出了若干面向?qū)ο笤O(shè)計(jì)原則來衡量代碼的優(yōu)劣:
- 開閉原則 OCP (The Open-Close Principle)
- 單一職責(zé)原則 SRP (Single Responsibility Principle)
- 依賴倒置原則 DIP (Dependence Inversion Principle)
- 最少知識(shí)原則 LKP (Least Knowledge Principle)) / 迪米特法則 (Law Of Demeter)
- 里氏替換原則 LSP (Liskov Substitution Principle)
- 接口隔離原則 ISP (Interface Segregation Principle)
- 組合/聚合復(fù)用原則 CARP (Composite/Aggregate Reuse Principle)
這些原則想必大家都很熟悉了,是我們編寫代碼時(shí)的指導(dǎo)方針,按照這些原則開發(fā)的代碼具有高內(nèi)聚低耦合的特性。換句話說,我們可以用這些原則來衡量代碼的優(yōu)劣。
但這些原則并不是死板的教條,我們也經(jīng)常會(huì)因?yàn)槠渌臋?quán)衡(例如可讀性、復(fù)雜度等)違背或者放棄一些原則。比如子類擁有特性的方法時(shí),我們很可能打破里氏替換原則。再比如,單一職責(zé)原則跟接口隔離原則有時(shí)候是沖突的,我們通常會(huì)舍棄接口隔離原則,保持單一職責(zé)。只要打破原則的理由足夠充分,也并不見得是壞的代碼。
4、OOP三大特征
4.1封裝
盡可能隱藏一個(gè)模塊的實(shí)現(xiàn)細(xì)節(jié)(屬性名稱,屬性是否可變,算法,數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)類型)
訪問控制只是為了防止程序員的無意誤用,不打算,也無法防止程序員的故意破壞
4.2繼承
繼承使用不當(dāng)會(huì)破壞封裝,造成信息泄露
先考慮組合,在考慮繼承
繼承是 behaves-like-a, is-substitutable-for 的關(guān)系,不是 is-a 或 is-a-kind-of 的關(guān)系
4.3多態(tài)
- 相同的實(shí)現(xiàn)代碼適用不同的場(chǎng)合
- 不同的實(shí)現(xiàn)代碼適用相同的場(chǎng)合
二、如何判斷不是好的代碼
討論了好代碼的必要條件,我們?cè)賮砜纯春么a的否定條件:什么不是好的代碼。Kent Beck 使用味道來形容重構(gòu)的時(shí)機(jī),我認(rèn)為當(dāng)代碼有壞味道的時(shí)候,也代表了其并不是好的代碼。
代碼的壞味道
► 重復(fù)
重復(fù)可能是軟件中一切邪惡的根源。—— Robert C.Martin
Martin Fowler 也認(rèn)為壞味道中首當(dāng)其沖的就是重復(fù)代碼。
很多時(shí)候,當(dāng)我們消除了重復(fù)代碼之后,發(fā)現(xiàn)代碼就已經(jīng)比原來整潔多了。
► 函數(shù)過長(zhǎng)、類過大、參數(shù)過長(zhǎng)
過長(zhǎng)的函數(shù)解釋能力、共享能力、選擇能力都較差,也不易維護(hù)。
過大的類代表了類做了很多事情,也常常有過多的重復(fù)代碼。
參數(shù)過長(zhǎng),不易理解,調(diào)用時(shí)也容易出錯(cuò)。
► 發(fā)散式變化、霰彈式修改、依戀情結(jié)
如果一個(gè)類不是單一職責(zé)的,則不同的變化可能都需要修改這個(gè)類,說明存在發(fā)散式變化,應(yīng)考慮將不同的變化分離開。
如果某個(gè)變化需要修改多個(gè)類的方法,則說明存在霰彈式修改,應(yīng)考慮將這些需要修改的方法放入同一個(gè)類。
如果函數(shù)對(duì)于某個(gè)類的興趣高于了自己所處的類,說明存在依戀情結(jié),應(yīng)考慮將函數(shù)轉(zhuǎn)移到他應(yīng)有的類中。
► 數(shù)據(jù)泥團(tuán)
有時(shí)候會(huì)發(fā)現(xiàn)三四個(gè)相同的字段,在多個(gè)類和函數(shù)中均出現(xiàn),這時(shí)候說明有必要給這一組字段建立一個(gè)類,將其封裝起來。
► 過多的 if...else 或者使用 switch
過多的 if...else 或者 switch ,都應(yīng)該考慮用多態(tài)來替換掉。甚至有些人認(rèn)為除個(gè)別情況外,代碼中就不應(yīng)該存在 if...else 。
三、總結(jié)
本文首先一句話概括了我認(rèn)為的好的優(yōu)雅代碼的必要條件:精簡(jiǎn),邏輯清晰,高內(nèi)聚,低耦合,接著具體分析了壞代碼的特點(diǎn),什么樣的代碼不是好的代碼。僅是本人的一些見解,希望對(duì)各位以后的編程有些許的幫助。
對(duì)于如何保持代碼整潔,離不開設(shè)計(jì)模式和代碼重構(gòu),多閱讀開源社區(qū)的代碼,比如最近微信開源的MMKV就可以讀來學(xué)習(xí),像世界同行大佬學(xué)習(xí)交流如何優(yōu)雅的寫代碼,也可以讀一些經(jīng)典的書籍如《代碼整潔之道》、《重構(gòu)改善既有代碼的設(shè)計(jì)》、《重構(gòu)改善既有代碼的設(shè)計(jì)》等等。






