寫在前面
這本書很早就看了猖辫,但看的斷斷續(xù)續(xù)的。這次疫情嚴重砚殿,在家無聊啃憎,打游戲工作之余重新完整看一遍,算是復(fù)習(xí)吧似炎。另外也是工作了不少時間了辛萍,也算是總結(jié)一下自己的經(jīng)驗吧。
筆記內(nèi)容
第一章 整潔代碼
書中通過各種舉例羡藐,來告訴我們一件事:時時保持代碼整潔贩毕。
從15年年末開始做游戲客戶端程序,自己寫過不少爛代碼传睹,也寫了些還算能看的代碼耳幢。
個人覺得莺褒,最最基本的就是要讓自己的代碼盡可能的干凈——先不談方法封裝之類的問題氧吐,只說格式——代碼的縮進是不是保持一致了脯爪,空行用的對不對,沒用的注釋代碼是不是可以刪掉邢隧,功能相近的函數(shù)方法是不是應(yīng)該放在一起……
我們寫的代碼不可避免的會有bug,但最起碼的冈在,是不是應(yīng)該讓我們的代碼看起來像好代碼呢倒慧。操作難度也不大,寫完新代碼檢查一遍包券,剪切粘貼幾下就好了纫谅,修改的代碼提交之前看下修改記錄,有沒用的代碼順手刪一刪溅固,幾分鐘的事付秕。
代碼干凈了,看起來也舒服侍郭,查bug也相對來說更好查一些询吴。
第二章 有意義的命名
命名算是程序中基礎(chǔ)中的基礎(chǔ)了吧掠河,好的命名能讓你的代碼更好理解。
- 命名不要隨意簡寫猛计,或者應(yīng)該讓變量名能夠自我描述這個變量是做什么的唠摹。
- 盡量不要起相似度非常大的名字,避免出現(xiàn)使用錯誤奉瘤。
- 保證命名的單詞拼寫是正確的勾拉,如果不是,及時改正盗温。
- 命名要簡潔藕赞,比如
name
就要比nameString
要好,省卻無用的類型名等肌访,會讓我們寫的更舒服找默。 - 一般來講,變量名和類名主體應(yīng)該是名詞吼驶,方法名主體是動詞惩激。
上面是簡單寫了寫書上的內(nèi)容。
就我個人而言蟹演,會盡可能的使用能自我描述的名字风钻,當(dāng)然這樣也無法避免的會產(chǎn)生一個問題——變量名過長——好在現(xiàn)在IDE都有自動補全,這不算大問題酒请。
上面的第4條骡技,對寫UI組件相關(guān)的東西時,并不是太適用羞反。我們的UI組件實在太多布朦,不加上諸如Text
Button
之類的可能會讓我們寫代碼變的很費勁。
還有昼窗,命名不要用拼音漢字是趴,遇到不知道的單詞查一下就好了。(當(dāng)然澄惊,拼音有時候還是可以用的唆途,比如說銀行稅務(wù)局那種專業(yè)性特別強的項目,一些變量翻譯成英文那真是又臭又長掸驱,縮寫還不好認肛搬,這時候可能拼音就好一些,當(dāng)然漢字就不要用了毕贼,寫代碼時來回切換輸入法不嫌煩么温赔?)
第三章 函數(shù)
- 函數(shù)要盡可能的短小
- 每個函數(shù)只做一件事
- 函數(shù)之間的順序要調(diào)整好,符合從上到下的閱讀順序
- 函數(shù)的參數(shù)越少越好
- 盡可能不要用
out
ref
帅刀,有返回值就直接返回 - 盡可能不要向函數(shù)中傳標(biāo)識參數(shù)让腹,如果真的需要远剩,可以考慮將函數(shù)拆成多個
- 如果參數(shù)必須出現(xiàn)多個,可以考慮將一些參數(shù)封裝到一個類里面
函數(shù)短小的好處是顯而易見的骇窍,一方面瓜晤,它能夠縮短“主功能函數(shù)”的長度,方便閱讀腹纳,另一方面痢掠,短小功能單一的函數(shù)方便我們進行復(fù)用。
工作中嘲恍,我會習(xí)慣性的把界面邏輯拆成若干塊足画,每塊負責(zé)一個部分;涉及到的計算部分單獨寫一個函數(shù)——即使它只在這塊界面里使用佃牛;涉及到數(shù)據(jù)修改的單獨寫一個函數(shù)或者類淹辞。
做過客戶端的應(yīng)該都清楚,函數(shù)拆的夠細的話俘侠,復(fù)用不復(fù)用的先不說象缀,起碼后面改bug的時候改動會小一些,避免因為改bug而導(dǎo)致一堆bug爷速。而且客戶端的表現(xiàn)效果隨時可能會改央星,盡可能將不受界面影響的地方寫在函數(shù)里面,也利于加快自己后期的迭代效率惫东。
有些時候界面里的元素很多莉给,聲明的變量和用到這個變量的函數(shù)相隔太遠,我們寫代碼時就會不舒服廉沮。我的習(xí)慣是使用region
來分塊颓遏,將一部分變量和使用這些變量的函數(shù)放到一個塊里,方便寫滞时,也方便讀州泊。
另外,必要的函數(shù)及時添加注釋漂洋,說明這個函數(shù)是干什么的,每個參數(shù)的意義是什么力喷,返回值是什么刽漂。
最后就是函數(shù)命名和這個函數(shù)做的事情要符合,不要函數(shù)名字是A弟孟,結(jié)果里面還干了B的事贝咙,這容易導(dǎo)致一些Bug。
總之拂募,就是盡可能不要寫長函數(shù)庭猩,一般不要超過20行吧窟她。
第四章 注釋
- 能不寫注釋就不要寫注釋
如果我們命名足夠規(guī)范,且能夠自行解釋的話蔼水,注釋基本就是無用&重復(fù)的震糖。 - 寫了的注釋及時更新
不要一個函數(shù)已經(jīng)改了名字,功能趴腋,但它的注釋仍然是舊的吊说。 - 注釋掉的代碼記得及時刪除
客戶端實際開發(fā)過程中,確實會因為表現(xiàn)效果的改過來改過去而臨時注釋代碼优炬,但版本定下來后颁井,記得把臨時注釋的代碼刪除掉,避免干擾正常代碼蠢护。 - 復(fù)雜的算法要寫上注釋
一是為了避免自己忘掉雅宾,二是方便他人修改
個人經(jīng)驗來說,功能接口的注釋一般會有葵硕。界面邏輯中眉抬,復(fù)雜的計算的地方會注釋每一步做了什么。功能開發(fā)調(diào)試時有時候會加上TODO獲取其他的注釋標(biāo)記贬芥。其他的基本就沒什么了吐辙。
總的來說,寫代碼的時候蘸劈,將名字起的好一點昏苏,一般用不著額外注釋。
第五章 格式
格式不分好壞威沫,而在于統(tǒng)一贤惯。
實際開發(fā)中,如果規(guī)范有通用代碼風(fēng)格文檔的話棒掠,就按照文檔來孵构。沒有的話就和其他同事的風(fēng)格保持一致就好了。如果是自己新啟的項目烟很,那么你要保證自己的代碼風(fēng)格始終是一致的颈墅。
我寫自己的代碼,習(xí)慣是按照公有方法雾袱,私有方法恤筛,公有變量,私有變量的順序排序芹橡。公司代碼的話毒坛,一般是每塊中變量在上,方法在下。
private
我不會主動去寫煎殷,因為沒必要屯伞,默認就是私有,額外敲個單詞我嫌費手豪直。當(dāng)然如果IDE自動生成的我也不會去主動刪劣摇,同樣是因為沒必要,刪除一樣費手顶伞。
此外不同語言的推薦代碼風(fēng)格也不一樣饵撑,一般按照推薦的代碼風(fēng)格就可以。
代碼寫完之后注意調(diào)整一下代碼順序唆貌,功能相關(guān)的函數(shù)放在一起滑潘,方便查看。
第六章 對象和數(shù)據(jù)結(jié)構(gòu)
- 隱藏實現(xiàn)并不僅僅是在變量之間放上一層函數(shù)那么簡單锨咙。隱藏實現(xiàn)關(guān)乎抽象语卤。類并不簡單的將變量通過賦值器和取值器推向外部,而是暴露抽象接口酪刀。使用戶無需了解數(shù)據(jù)的實現(xiàn)就能操作數(shù)據(jù)的本體粹舵。
- 對象將數(shù)據(jù)隱藏在抽象之后,數(shù)據(jù)結(jié)構(gòu)暴露其數(shù)據(jù)且沒有具體意義的函數(shù)接口骂倘。
一個算是能夠區(qū)分對象和數(shù)據(jù)結(jié)構(gòu)的方法吧:對象就應(yīng)該能做些什么眼滤,而數(shù)據(jù)結(jié)構(gòu)僅僅只是數(shù)據(jù)而已 - 過程式代碼(使用數(shù)據(jù)結(jié)構(gòu)的代碼)便于在不改動當(dāng)前數(shù)據(jù)結(jié)構(gòu)的前提下添加新的函數(shù);面向?qū)ο蟮拇a便于在不改變當(dāng)前已有函數(shù)的情況下添加新類历涝。
一切皆對象只是一個傳說诅需,要根據(jù)實際情況來使用。 - 避免開火車式的函數(shù)調(diào)用荧库。類似于
A()
B()
C()
返回的對象不一樣的情況下這樣A().B().C()
調(diào)用堰塌。(方法不應(yīng)該調(diào)用由任何函數(shù)返回的對象的方法——只和朋友談話,不與陌生人談話——得墨忒定律)
書中談到了一個值得注意的地方:如果數(shù)據(jù)結(jié)構(gòu)僅僅是簡單的擁有公有變量而沒有函數(shù)分衫,對象擁有私有變量和公有函數(shù)场刑,這樣我們就不容易產(chǎn)生混淆。
這個我自己寫代碼的時候也確實出過類似的問題——濫用屬性訪問器——有些簡單的讀寫數(shù)據(jù)也加上屬性訪問器著實有些多余蚪战,這方面確實應(yīng)該注意一下牵现。 - 避免混雜
詳細來說,就是不要一半是數(shù)據(jù)結(jié)構(gòu)邀桑,一半是對象施籍。這樣的代碼相當(dāng)于結(jié)合了上面第3條的所有缺點——既不方便擴展類,也不方便擴展函數(shù)接口概漱。 - 幾種數(shù)據(jù)結(jié)構(gòu)的形式
數(shù)據(jù)傳送對象(DTO):只有公有變量,沒有函數(shù)的數(shù)據(jù)結(jié)構(gòu)
豆結(jié)構(gòu)(bean):有簡單的屬性訪問器
Active Record:特殊的DTO喜喂,除了公有變量外還有一些save瓤摧,find之類的查找方法
看完這一章覺得收獲不小竿裂,之前自己寫代碼確實沒有注意數(shù)據(jù)結(jié)構(gòu)和對象之間的區(qū)別,也確實因為這個原因犯過一些錯誤照弥,值得注意腻异。
第七章 錯誤處理
- 如果錯誤處理影響了正常邏輯的表達,那么這段錯誤處理就是不好的这揣。
- 如果有過多的錯誤處理悔常,可以考慮將錯誤直接拋出異常,然后使用
try
catch
给赞。 - 不要向函數(shù)中傳遞
null
机打。 - 函數(shù)不要返回
null
。
可以考慮在函數(shù)返回null的地方返回一個特例類片迅,或者直接將null
拋出異常残邀。如果是第三方API返回的,可以考慮將接口額外封裝柑蛇,單獨處理一下芥挣。
就個人工作經(jīng)驗來說,null
的處理真的是各種多耻台。&&好像自己很忌諱寫try catch
空免,這方面確實應(yīng)該改正。
順便網(wǎng)上查了查關(guān)于try catch
的效率問題盆耽,發(fā)現(xiàn)try
的部分基本沒影響蹋砚,好多if return
的錯誤處理改用異常的話能讓代碼簡潔不少。
另外征字,不要在開發(fā)中隱藏錯誤都弹,自己開發(fā)時,一定要讓報錯明顯的清晰的展示出來——比如什么地方數(shù)據(jù)空了匙姜,一定要有相應(yīng)的log或者error畅厢,將不正常的地方暴露出來才有助于我們提升代碼的穩(wěn)固性。
第八章 邊界
- 使用第三方代碼時氮昧,可以將第三方的代碼接口按照我們的需求進行一次封裝框杜。這么做是為了隔離代碼,防止后更新第三方代碼是袖肥,第三方代碼API變化而導(dǎo)致的大范圍修改咪辱。
- 有些時候,做新功能需要某個尚未完成的模塊做支持椎组。這時候可以先定義尚未完成的模塊的接口油狂,先完成自己的功能開發(fā),等需要的模塊完成后在修改接口實現(xiàn)。
-
學(xué)習(xí)型測試:不在生產(chǎn)代碼中實驗新的東西专筷,而是編寫測試來理解和明確第三方API的功能弱贼。這樣做一是更容易清楚API的功能,二是當(dāng)?shù)谌紸PI更新時磷蛹,可以使用這些測試代碼來檢查API接口以及實現(xiàn)是否有變化吮旅。
平常自己寫代碼的時候確實沒有寫正式的測試腳本,基本上都是臨時測試味咳,測試通過后測試代碼就被干掉了庇勃,這方面確實應(yīng)該改正。
第九章 單元測試
- 單元測試讓代碼可拓展槽驶,可維護责嚷,可復(fù)用——在有測試的基礎(chǔ)上,放心的改動任何代碼捺檬,而不用擔(dān)心自己的改動導(dǎo)致一些莫名其妙的Bug——你只需要改動后也能通過測試接好了再层。
- TDD三定律
書上的中文翻譯實在讓人頭大,所以這里記錄的是英文版的&&網(wǎng)上的總結(jié)性的翻譯堡纬。- You must write a failing unit test before you write production code.(測試先行)
- You must stop writing that unit test as soon as it fails; and not compiling is failing.(測試不通過聂受,寫生產(chǎn)代碼)
- You must stop writing production code as soon as the currently failing test passes.(老測試通過,開始寫新的測試)
- 測試代碼一樣需要維護和整潔:臟測試 <= 沒測試
- 測試代碼的要求:可讀性烤镐。
- 測試代碼的步驟:構(gòu)造——操作——檢驗
- 測試代碼中斷言數(shù)量應(yīng)該最小化蛋济;每個測試代碼中只測試一個概念。
- 測試代碼FIRST原則
- Fast快速:測試能快速運行
- Independent獨立:測試代碼之間相互獨立炮叶,沒有關(guān)聯(lián)碗旅。
- Repeatable可重復(fù):測試代碼的應(yīng)該能夠在任何環(huán)境下重復(fù)運行。
- Self-Validating自足驗證:測試代碼有明確的測試成功還是失敗的輸出镜悉,而不是靠Log人工去驗證結(jié)果祟辟。
- Timely及時:測試代碼應(yīng)該在生產(chǎn)代碼前及時編寫。
說起來有些遺憾侣肄,平時工作中沒用過TDD進行開發(fā)旧困。時間緊,需求變更快稼锅,寫一般項目代碼時基本上就不寫測試代碼吼具,直接上的。估計以后工作中也不會有機會用TDD來進行開發(fā)矩距。還是盡可能在自己的項目中試驗吧拗盒。
第十章 類
- 類應(yīng)該盡量短小。
- 單一權(quán)責(zé)原則:類或者模塊有且僅有一個被修改的理由锥债。
- 內(nèi)聚:類應(yīng)該有少量的實體變量陡蝇,類中的每個方法都應(yīng)該操作一個或者多個實體變量痊臭。
將大函數(shù)改成小函數(shù)時,往往也是將大類拆成小類的時機毅整。 - 開閉原則:對擴展開放趣兄,對修改關(guān)閉。
理想系統(tǒng)中悼嫉,我們通過擴展代碼而不是修改代碼來為程序增加新的特性。 - 依賴倒置原則:上層模塊不依賴底層模塊拼窥,都應(yīng)該依賴抽象戏蔑;抽象不依賴細節(jié),細節(jié)依賴抽象鲁纠。
個人認為核心的還是要讓類盡可能的小总棵。一方面是修改功能的時候,只需要修改對應(yīng)的小類就喊了改含;另一方面情龄,小的類才方便更精準(zhǔn)的復(fù)用。
第十一章 系統(tǒng)
這個部分書上的好多東西是java的捍壤,本人沒正經(jīng)做過java開發(fā)骤视,只是懂一點java語法罷了,同時系統(tǒng)這塊自己還有不少提升的余地鹃觉,這部分就記錄一下自己的經(jīng)驗&感受吧专酗。
系統(tǒng)這塊關(guān)注的是Main
入口,以及各個功能模塊之間的使用方式盗扇。
書中反復(fù)提到的一個關(guān)鍵點在于構(gòu)造和使用分離祷肯,也就是說,我們不要在Main中進行具體的處理疗隶,而是應(yīng)該將具體處理的邏輯交給指定的功能模塊佑笋。(這個錯誤自己曾經(jīng)犯過,當(dāng)時確實想的是分模塊啟動斑鼻,但是里面有些模塊之間有依賴關(guān)系蒋纬,當(dāng)時自己是直接把依賴的一些處理放在Main里面了,在不斷的迭代修改的情況下卵沉,Main中的代碼越來越亂颠锉,啟動邏輯各種復(fù)雜)。
還有就是一些參數(shù)之類的史汗,能走配置就走配置琼掠,不要直接代碼里面寫死,避免因為參數(shù)變化而反復(fù)改代碼停撞,不容易調(diào)試還容易出錯瓷蛙。
第十二章 迭進
- 先記錄幾個英文縮寫 SRP單一職責(zé)原則悼瓮,DIP依賴倒置原則,OO面向接口編程艰猬。
-
簡單設(shè)計的四條原則(四條原則重要性遞減)
- 運行所有測試
首先程序要能按照預(yù)期正常的工作横堡。這里額外強調(diào)了測試,是因為測試導(dǎo)向我們編寫單一短小的設(shè)計方案(SRP)冠桃,也因為緊耦合的代碼難以測試命贴,也一樣會導(dǎo)向我們盡可能的解耦合(DIP)。測試消除了對清理代碼就會破壞代碼的恐懼食听。 - 不可重復(fù)
重復(fù)的代碼胸蛛,重復(fù)的邏輯,重復(fù)的功能樱报,都應(yīng)該盡可能的消除掉葬项。拿現(xiàn)在在做的項目舉例子吧,舊的代碼中有好多相同功能的不同接口迹蛤,同一個數(shù)據(jù)不同的訪問路徑民珍,尤其是各種局部的中間緩存,特別容易出現(xiàn)數(shù)據(jù)沒同步到全局的Bug盗飒。重復(fù)的越少嚷量,改bug就越容易(不然一個Bug要改N多個地方,容易錯漏)箩兽。 - 表達了程序員的意圖
自己寫的代碼要清晰易懂津肛。注意命名,保持函數(shù)和類的短小汗贫,必要時使用標(biāo)準(zhǔn)命名法身坐,編寫單元測試(單元測試的一個重要作用就是通過實例起到文檔的作用)。此外落包,必要的地方寫上注釋部蛇,尤其是原創(chuàng)和半原創(chuàng)的稀奇古怪的算法。 - 盡可能少的類和函數(shù)的數(shù)量
在上面三條都滿足的情況下咐蝇,可能會有很多小類和小函數(shù)涯鲁。但類和函數(shù)的數(shù)量太多,有些時候完全是我們的教條主義導(dǎo)致的有序,不要因為編碼的標(biāo)準(zhǔn)限制而寫一堆無用的代碼抹腿。
- 運行所有測試
第十三章 并發(fā)編程
本來是想記一下書上的關(guān)鍵點的,后來發(fā)現(xiàn)僅僅是記錄一些名詞旭寿,也沒什么用警绩,就簡單寫些自己想到的吧。
服務(wù)器的東西接觸過一些盅称,但修改的都是純邏輯相關(guān)的內(nèi)容肩祥,并沒有涉及多線程進程的內(nèi)容后室。
自己本身是Unity客戶端,曾經(jīng)獨立寫過一個項目的網(wǎng)絡(luò)模塊混狠,算是有一點點經(jīng)驗吧岸霹。
先來說為什么要用多線程。網(wǎng)絡(luò)連接将饺,網(wǎng)絡(luò)消息發(fā)送和接收贡避,多數(shù)都是阻塞的——阻塞階段你的app什么都干不了——表現(xiàn)就是卡頓或者卡死。
至于客戶端的線程共享數(shù)據(jù)的處理予弧,一般來說加上lock
就可以了——防止主線程查詢數(shù)據(jù)與網(wǎng)絡(luò)線程存放數(shù)據(jù)相沖突贸桶。數(shù)據(jù)方面其他的沒什么了。
覺得最后就是代碼的梳理部分了桌肴。不要將不同線程的邏輯代碼混淆在一起,容易出現(xiàn)Bug琉历。
現(xiàn)在來反思自己之前寫的網(wǎng)絡(luò)部分坠七,還是有些問題的。這里記錄下來旗笔,避免以后再犯彪置。
程序入口部分,因為網(wǎng)絡(luò)做了不少回調(diào)消息之類的處理蝇恶,影響了總體的流程拳魁。
網(wǎng)絡(luò)模塊有部分代碼分割的不夠好,寫的有些亂撮弧。
過于強調(diào)封裝潘懊,有一些沒用的工具方法 ,憑空增加了代碼的復(fù)雜度贿衍。
第十四章 逐步改進授舟;第十六章 重構(gòu)
- 首先保證程序能夠正常的工作。
- 添加新功能特性后 贸辈,不止要看程序能不能正常工作释树,還要看代碼是不是因為這個添加而亂掉了。
- 修改代碼不僅僅是代碼干凈就可以的擎淤,還要考慮到以后的拓展性奢啥。我們每一次修改代碼都應(yīng)該是為了未來少修改代碼(就像是努力是為了以后不用努力一樣)。
- 在不斷的功能添加和代碼修改中嘴拢,測試能夠保障我們不會把原本好用的功能邏輯改壞掉桩盲。進一步說明了測試的重要性。
- 不斷修改優(yōu)化自己的舊代碼對自己的技術(shù)成長的幫助并不比仔細構(gòu)思寫新代碼小炊汤。不斷修改自己的代碼正驻,明確自己之前犯過的錯誤弊攘,從而減少自己再次犯錯的幾率。
第十七章 味道與啟發(fā)
這一章的內(nèi)容是總結(jié)式的姑曙,好多點前面都有提到過襟交。下面主要記錄一下之前沒有提到過并且自己還沒有做到的內(nèi)容。
- 程序的啟動伤靠,出包等各個流程步驟要足夠簡單捣域,足夠快。(復(fù)雜==不好用==懶得用==測試少==bug多)
- 程序的測試步驟也要足夠簡單宴合。
- 注意編譯警告焕梅,有時程序錯誤查不到原因就是因為我們忽略了警告。
- 注意邊界條件的檢查卦洽,注意邊界條件的封裝贞言。
- 盡可能限制類和模塊暴露的接口數(shù)量。類中方法越少越好阀蒂,函數(shù)知道的變量越少越好该窗,類擁有的實體變量越少越好。
- 設(shè)定一套命名的規(guī)范蚤霞,并且從始至終使用
- 類中方法一般應(yīng)該只使用自己類中的參數(shù)和方法酗失,而不應(yīng)該使用其他類中的參數(shù)和方法。
- 盡量使用多態(tài)來替代switch/case昧绣。
- 將if的判斷條件封裝成函數(shù)方法 规肴,使代碼更易讀。
- 避免否定性條件判斷
- 不要掩蓋時序耦合夜畴,把它暴露出來
- 命名應(yīng)該說明副作用拖刃。eg.
GetData()
和GetOrCreateData()
,后一個明確地告訴你如果獲取不到會新建一個給你 斩启。 - 缺陷通常扎堆序调,某個地方有缺陷時,多次檢查兔簇,也許還有別的 发绢。
完畢