leader:你的代码太烂了我根本看不懂
一、可讀性的重要性
編程有很大一部分時間是在閱讀代碼,不僅要閱讀自己的代碼,而且要閱讀別人的代碼。因此,可讀性良好的代碼能夠大大提高編程效率。
可讀性良好的代碼往往會讓代碼架構(gòu)更好,因為程序員更愿意去修改這部分代碼,而且也更容易修改。
只有在核心領(lǐng)域為了效率才可以放棄可讀性,否則可讀性是第一位。
二、用名字表達(dá)代碼含義
一些比較有表達(dá)力的單詞:
| send | deliver、dispatch、announce、distribute、route |
| find | search、extract、locate、recover |
| start | launch、create、begin、open |
| make | create、set up、build、generate、compose、add、new |
使用 i、j、k 作為循環(huán)迭代器的名字過于簡單,user_i、member_i 這種名字會更有表達(dá)力。因為循環(huán)層次越多,代碼越難理解,有表達(dá)力的迭代器名字可讀性會更高。
為名字添加形容詞等信息能讓名字更具有表達(dá)力,但是名字也會變長。名字長短的準(zhǔn)則是:作用域越大,名字越長。因此只有在短作用域才能使用一些簡單名字。
三、名字不能帶來歧義
起完名字要思考一下別人會對這個名字有何解讀,會不會誤解了原本想表達(dá)的含義。
布爾相關(guān)的命名加上 is、can、should、has 等前綴。
-
用 min、max 表示數(shù)量范圍;
-
用 first、last 表示訪問空間的包含范圍;
-
begin、end 表示訪問空間的排除范圍,即 end 不包含尾部。
四、良好的代碼風(fēng)格
適當(dāng)?shù)目招泻涂s進(jìn)。
排列整齊的注釋:
int a = 1; // 注釋 int b = 11; // 注釋 int c = 111; // 注釋語句順序不能隨意,比如與 html 表單相關(guān)聯(lián)的變量的賦值應(yīng)該和表單在 html 中的順序一致。
五、為何編寫注釋
閱讀代碼首先會注意到注釋,如果注釋沒太大作用,那么就會浪費(fèi)代碼閱讀的時間。那些能直接看出含義的代碼不需要寫注釋,特別是不需要為每個方法都加上注釋,比如那些簡單的 getter 和 setter 方法,為這些方法寫注釋反而讓代碼可讀性更差。
不能因為有注釋就隨便起個名字,而是爭取起個好名字而不寫注釋。
可以用注釋來記錄采用當(dāng)前解決辦法的思考過程,從而讓讀者更容易理解代碼。
注釋用來提醒一些特殊情況。
用 TODO 等做標(biāo)記:
| TODO | 待做 |
| FIXME | 待修復(fù) |
| HACK | 粗糙的解決方案 |
| XXX | 危險!這里有重要的問題 |
六、如何編寫注釋
盡量簡潔明了:
// The first String is student's name // The Second Integer is student's score Map<String, Integer> scoreMap = new HashMap<>(); // Student's name -> Student's score Map<String, Integer> scoreMap = new HashMap<>();添加測試用例來說明:
// ... // Example: add(1, 2), return 3 int add(int x, int y) {return x + y; }使用專業(yè)名詞來縮短概念上的解釋,比如用設(shè)計模式名來說明代碼。
七、提高控制流的可讀性
條件表達(dá)式中,左側(cè)是變量,右側(cè)是常數(shù)。比如下面第一個語句正確:
if (len < 10) if (10 > len)只有在邏輯簡單的情況下使用 ? : 三目運(yùn)算符來使代碼更緊湊,否則應(yīng)該拆分成 if / else;
do / while 的條件放在后面,不夠簡單明了,并且會有一些迷惑的地方,最好使用 while 來代替。
如果只有一個 goto 目標(biāo),那么 goto 尚且還能接受,但是過于復(fù)雜的 goto 會讓代碼可讀性特別差,應(yīng)該避免使用 goto。
在嵌套的循環(huán)中,用一些 return 語句往往能減少嵌套的層數(shù)。
八、拆分長表達(dá)式
長表達(dá)式的可讀性很差,可以引入一些解釋變量從而拆分表達(dá)式:
if line.split(':')[0].strip() == "root":... username = line.split(':')[0].strip() if username == "root":...使用摩根定理簡化一些邏輯表達(dá)式:
if (!a && !b) {... } if (!(a || b)) {... }九、變量與可讀性
去除控制流變量 。在循環(huán)中通過使用 break 或者 return 可以減少控制流變量的使用。
boolean done = false; while (/* condition */ && !done) {...if ( ... ) {done = true;continue;} } while(/* condition */) {...if ( ... ) {break;} }減小變量作用域 。作用域越小,越容易定位到變量所有使用的地方。
JavaScript 可以用閉包減小作用域。以下代碼中 submit_form 是函數(shù)變量,submitted 變量控制函數(shù)不會被提交兩次。第一個實現(xiàn)中 submitted 是全局變量,第二個實現(xiàn)把 submitted 放到匿名函數(shù)中,從而限制了起作用域范圍。
submitted = false; var submit_form = function(form_name) {if (submitted) {return;}submitted = true; }; var submit_form = (function() {var submitted = false;return function(form_name) {if(submitted) {return;}submitted = true;} }()); // () 使得外層匿名函數(shù)立即執(zhí)行JavaScript 中沒有用 var 聲明的變量都是全局變量,而全局變量很容易造成迷惑,因此應(yīng)當(dāng)總是用 var 來聲明變量。
變量定義的位置應(yīng)當(dāng)離它使用的位置最近。
實例解析
在一個網(wǎng)頁中有以下文本輸入字段:
<input type = "text" id = "input1" value = "a"> <input type = "text" id = "input2" value = "b"> <input type = "text" id = "input3" value = ""> <input type = "text" id = "input4" value = "d">現(xiàn)在要接受一個字符串并把它放到第一個空的 input 字段中,初始實現(xiàn)如下:
var setFirstEmptyInput = function(new_alue) {var found = false;var i = 1;var elem = document.getElementById('input' + i);while (elem != null) {if (elem.value === '') {found = true;break;}i++;elem = document.getElementById('input' + i);}if (found) elem.value = new_value;return elem; }以上實現(xiàn)有以下問題:
- found 可以去除;
- elem 作用域過大;
- 可以用 for 循環(huán)代替 while 循環(huán);
十、抽取函數(shù)
工程學(xué)就是把大問題拆分成小問題再把這些問題的解決方案放回一起。
首先應(yīng)該明確一個函數(shù)的高層次目標(biāo),然后對于不是直接為了這個目標(biāo)工作的代碼,抽取出來放到獨(dú)立的函數(shù)中。
介紹性的代碼:
int findClostElement(int[] arr) {int clostIdx;int clostDist = Interger.MAX_VALUE;for (int i = 0; i < arr.length; i++) {int x = ...;int y = ...;int z = ...;int value = x * y * z;int dist = Math.sqrt(Math.pow(value, 2), Math.pow(arr[i], 2));if (dist < clostDist) {clostIdx = i;clostDist = value;}}return clostIdx; }以上代碼中循環(huán)部分主要計算距離,這部分不屬于代碼高層次目標(biāo),高層次目標(biāo)是尋找最小距離的值,因此可以把這部分代替提取到獨(dú)立的函數(shù)中。這樣做也帶來一個額外的好處有:可以單獨(dú)進(jìn)行測試、可以快速找到程序錯誤并修改。
public int findClostElement(int[] arr) {int clostIdx;int clostDist = Interger.MAX_VALUE;for (int i = 0; i < arr.length; i++) {int dist = computDist(arr, i);if (dist < clostDist) {clostIdx = i;clostDist = value;}}return clostIdx; }并不是函數(shù)抽取的越多越好,如果抽取過多,在閱讀代碼的時候可能需要不斷跳來跳去。只有在當(dāng)前函數(shù)不需要去了解某一塊代碼細(xì)節(jié)而能夠表達(dá)其內(nèi)容時,把這塊代碼抽取成子函數(shù)才是好的。
函數(shù)抽取也用于減小代碼的冗余。
十一、一次只做一件事
只做一件事的代碼很容易讓人知道其要做的事;
基本流程:列出代碼所做的所有任務(wù);把每個任務(wù)拆分到不同的函數(shù),或者不同的段落。
十二、用自然語言表述代碼
先用自然語言書寫代碼邏輯,也就是偽代碼,然后再寫代碼,這樣代碼邏輯會更清晰。
十三、減少代碼量
不要過度設(shè)計,編碼過程會有很多變化,過度設(shè)計的內(nèi)容到最后往往是無用的。
多用標(biāo)準(zhǔn)庫實現(xiàn)。
參考資料
- Dustin, Boswell, Trevor, 等. 編寫可讀代碼的藝術(shù) [M]. 機(jī)械工業(yè)出版社, 2012.
總結(jié)
以上是生活随笔為你收集整理的leader:你的代码太烂了我根本看不懂的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《刀剑神域虚空幻界》技能融合方法解析及练
- 下一篇: 《战锤40K战争黎明3》各阵营技能效果介