年薪十萬與年薪百萬程序員寫的代碼的區別
導讀:編程是一門創造性的工作,是一門藝術。我們每天與代碼打交道,為什么普通碼農辛苦一年只拿十萬,而高級架構師年薪百萬。最主要的就是我們敲出來的代碼有差別,差別在意大部分碼農敲出來壞的代碼,而高級架構師能敲出優雅的好的代碼。我們每天都會敲代碼,但當被問道什么是好的優雅的代碼時,大家可能會先愣一下,然后給出的回答要么比較空泛,要么比較散,沒辦法簡單明了地概括出來。顯然,這個問題并沒有唯一的標準答案,誰都可以談論自己的理解。要成為合格的架構師最基本的要求是能寫好的優雅的代碼,所以必須要知道什么是優雅代碼。這篇文章我來分享一下阿里系高級架構師對于好的優雅代碼的理解。

一句話概括
衡量代碼質量的唯一有效標準:WTF/min —— Robert C. Martin

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