leader:你的代碼太爛了我根本看不懂

一、可讀性的重要性

編程有很大一部分時間是在閱讀代碼,不僅要閱讀自己的代碼,而且要閱讀別人的代碼。因此,可讀性良好的代碼能夠大大提高編程效率。

可讀性良好的代碼往往會讓代碼架構更好,因為程序員更愿意去修改這部分代碼,而且也更容易修改。

只有在核心領域為了效率才可以放棄可讀性,否則可讀性是第一位。

二、用名字表達代碼含義

一些比較有表達力的單詞:

單詞可替代單詞
senddeliver、dispatch、announce、distribute、route
findsearch、extract、locate、recover
startlaunch、create、begin、open
makecreate、set up、build、generate、compose、add、new

使用 i、j、k 作為循環迭代器的名字過于簡單,user_i、member_i 這種名字會更有表達力。因為循環層次越多,代碼越難理解,有表達力的迭代器名字可讀性會更高。

為名字添加形容詞等信息能讓名字更具有表達力,但是名字也會變長。名字長短的準則是:作用域越大,名字越長。因此只有在短作用域才能使用一些簡單名字。

三、名字不能帶來歧義

起完名字要思考一下別人會對這個名字有何解讀,會不會誤解了原本想表達的含義。

布爾相關的命名加上 is、can、should、has 等前綴。

  • 用 min、max 表示數量范圍;

  • 用 first、last 表示訪問空間的包含范圍;

  • begin、end 表示訪問空間的排除范圍,即 end 不包含尾部。

四、良好的代碼風格

適當的空行和縮進。

排列整齊的注釋:

int a = 1;   // 注釋
int b = 11;  // 注釋
int c = 111; // 注釋

語句順序不能隨意,比如與 html 表單相關聯的變量的賦值應該和表單在 html 中的順序一致。

五、為何編寫注釋

閱讀代碼首先會注意到注釋,如果注釋沒太大作用,那么就會浪費代碼閱讀的時間。那些能直接看出含義的代碼不需要寫注釋,特別是不需要為每個方法都加上注釋,比如那些簡單的 getter 和 setter 方法,為這些方法寫注釋反而讓代碼可讀性更差。

不能因為有注釋就隨便起個名字,而是爭取起個好名字而不寫注釋。

可以用注釋來記錄采用當前解決辦法的思考過程,從而讓讀者更容易理解代碼。

注釋用來提醒一些特殊情況。

用 TODO 等做標記:

標記用法
TODO待做
FIXME待修復
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;
}

使用專業名詞來縮短概念上的解釋,比如用設計模式名來說明代碼。

七、提高控制流的可讀性

條件表達式中,左側是變量,右側是常數。比如下面第一個語句正確:

if (len < 10)
if (10 > len)

只有在邏輯簡單的情況下使用 ? : 三目運算符來使代碼更緊湊,否則應該拆分成 if / else;

do / while 的條件放在后面,不夠簡單明了,并且會有一些迷惑的地方,最好使用 while 來代替。

如果只有一個 goto 目標,那么 goto 尚且還能接受,但是過于復雜的 goto 會讓代碼可讀性特別差,應該避免使用 goto。

在嵌套的循環中,用一些 return 語句往往能減少嵌套的層數。

八、拆分長表達式

長表達式的可讀性很差,可以引入一些解釋變量從而拆分表達式:

if line.split(':')[0].strip() == "root":...
username = line.split(':')[0].strip()
if username == "root":...

使用摩根定理簡化一些邏輯表達式:

if (!a && !b) {...
}
if (!(a || b)) {...
}

九、變量與可讀性

去除控制流變量 。在循環中通過使用 break 或者 return 可以減少控制流變量的使用。

boolean done = false;
while (/* condition */ && !done) {...if ( ... ) {done = true;continue;}
}
while(/* condition */) {...if ( ... ) {break;}
}

減小變量作用域 。作用域越小,越容易定位到變量所有使用的地方。

JavaScript 可以用閉包減小作用域。以下代碼中 submit_form 是函數變量,submitted 變量控制函數不會被提交兩次。第一個實現中 submitted 是全局變量,第二個實現把 submitted 放到匿名函數中,從而限制了起作用域范圍。

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;}
}());  // () 使得外層匿名函數立即執行

JavaScript 中沒有用 var 聲明的變量都是全局變量,而全局變量很容易造成迷惑,因此應當總是用 var 來聲明變量。

變量定義的位置應當離它使用的位置最近。

實例解析

在一個網頁中有以下文本輸入字段:

<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">

現在要接受一個字符串并把它放到第一個空的 input 字段中,初始實現如下:

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;
}

以上實現有以下問題:

  • found 可以去除;
  • elem 作用域過大;
  • 可以用 for 循環代替 while 循環;
var setFirstEmptyInput = function(new_value) {for (var i = 1; true; i++) {var elem = document.getElementById('input' + i);if (elem === null) {return null;}if (elem.value === '') {elem.value = new_value;return elem;}}
};

十、抽取函數

工程學就是把大問題拆分成小問題再把這些問題的解決方案放回一起。

首先應該明確一個函數的高層次目標,然后對于不是直接為了這個目標工作的代碼,抽取出來放到獨立的函數中。

介紹性的代碼:

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;
}

以上代碼中循環部分主要計算距離,這部分不屬于代碼高層次目標,高層次目標是尋找最小距離的值,因此可以把這部分代替提取到獨立的函數中。這樣做也帶來一個額外的好處有:可以單獨進行測試、可以快速找到程序錯誤并修改。

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;
}

并不是函數抽取的越多越好,如果抽取過多,在閱讀代碼的時候可能需要不斷跳來跳去。只有在當前函數不需要去了解某一塊代碼細節而能夠表達其內容時,把這塊代碼抽取成子函數才是好的。

函數抽取也用于減小代碼的冗余。

十一、一次只做一件事

只做一件事的代碼很容易讓人知道其要做的事;

基本流程:列出代碼所做的所有任務;把每個任務拆分到不同的函數,或者不同的段落。

十二、用自然語言表述代碼

先用自然語言書寫代碼邏輯,也就是偽代碼,然后再寫代碼,這樣代碼邏輯會更清晰。

十三、減少代碼量

不要過度設計,編碼過程會有很多變化,過度設計的內容到最后往往是無用的。

多用標準庫實現。

參考資料

  • Dustin, Boswell, Trevor, 等. 編寫可讀代碼的藝術 [M]. 機械工業出版社, 2012.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/443999.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/443999.shtml
英文地址,請注明出處:http://en.pswp.cn/news/443999.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java寫的MySQL數據庫備份和恢復代碼:

1.MySQL數據庫備份和恢復&#xff0c;java代碼實現&#xff1a;詳情見下面&#xff1a; package com.spring.util; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOExce…

leetcode541. 反轉字符串 II(字符串一律用py秒殺)

給定一個字符串 s 和一個整數 k&#xff0c;你需要對從字符串開頭算起的每隔 2k 個字符的前 k 個字符進行反轉。 如果剩余字符少于 k 個&#xff0c;則將剩余字符全部反轉。 如果剩余字符小于 2k 但大于或等于 k 個&#xff0c;則反轉前 k 個字符&#xff0c;其余字符保持原樣…

leetcode628. 三個數的最大乘積(簡單,我覺得你不會)

給你一個整型數組 nums &#xff0c;在數組中找出由三個數組成的最大乘積&#xff0c;并輸出這個乘積。 示例 1&#xff1a; 輸入&#xff1a;nums [1,2,3] 輸出&#xff1a;6 示例 2&#xff1a; 輸入&#xff1a;nums [1,2,3,4] 輸出&#xff1a;24 示例 3&#xff1a; …

leetcode453. 最小操作次數使數組元素相等(賊難的簡單題)

給定一個長度為 n 的 非空 整數數組&#xff0c;每次操作將會使 n - 1 個元素增加 1。找出讓數組所有元素相等的最小操作次數。 示例&#xff1a; 輸入&#xff1a; [1,2,3] 輸出&#xff1a; 3 解釋&#xff1a; 只需要3次操作&#xff08;注意每次操作會增加兩個元素的值&am…

leetcode設計鏈表,非常工整的實現你值得擁有

設計鏈表的實現。您可以選擇使用單鏈表或雙鏈表。單鏈表中的節點應該具有兩個屬性&#xff1a;val 和 next。val 是當前節點的值&#xff0c;next 是指向下一個節點的指針/引用。如果要使用雙向鏈表&#xff0c;則還需要一個屬性 prev 以指示鏈表中的上一個節點。假設鏈表中的所…

leetcode1669. 合并兩個鏈表

給你兩個鏈表 list1 和 list2 &#xff0c;它們包含的元素分別為 n 個和 m 個。 請你將 list1 中第 a 個節點到第 b 個節點刪除&#xff0c;并將list2 接在被刪除節點的位置。 下圖中藍色邊和節點展示了操作后的結果&#xff1a; 請你返回結果鏈表的頭指針。 示例 1&#xff…

leetcode1721. 交換鏈表中的節點

給你鏈表的頭節點 head 和一個整數 k 。 交換 鏈表正數第 k 個節點和倒數第 k 個節點的值后&#xff0c;返回鏈表的頭節點&#xff08;鏈表 從 1 開始索引&#xff09;。 示例 1&#xff1a; 輸入&#xff1a;head [1,2,3,4,5], k 2 輸出&#xff1a;[1,4,3,2,5] 示例 2&…

《一天聊一個設計模式》 單例

我是兔兔rabbit&#xff0c;關注我吧&#xff0c;給自己每天的進步找一個機會和理由&#xff0c;不要被無效信息淹沒 單例&#xff08;Singleton&#xff09; Intent 確保一個類只有一個實例&#xff0c;并提供該實例的全局訪問點。 Class Diagram 使用一個私有構造函數、一…

Eclipse如何卸載插件

Help ---> About Eclipse -->Installation details--->選中你的插件-->Uninstall..

leetcode1502. 判斷能否形成等差數列(小學生難度)

給你一個數字數組 arr 。 如果一個數列中&#xff0c;任意相鄰兩項的差總等于同一個常數&#xff0c;那么這個數列就稱為 等差數列 。 如果可以重新排列數組形成等差數列&#xff0c;請返回 true &#xff1b;否則&#xff0c;返回 false 。 示例 1&#xff1a; 輸入&#x…

leetcode1528. 重新排列字符串

給你一個字符串 s 和一個 長度相同 的整數數組 indices 。 請你重新排列字符串 s &#xff0c;其中第 i 個字符需要移動到 indices[i] 指示的位置。 返回重新排列后的字符串。 示例 1&#xff1a; 輸入&#xff1a;s "codeleet", indices [4,5,6,7,0,2,1,3] 輸出…

leetcode976. 三角形的最大周長(又是你得不到的簡單題)

給定由一些正數&#xff08;代表長度&#xff09;組成的數組 A&#xff0c;返回由其中三個長度組成的、面積不為零的三角形的最大周長。 如果不能形成任何面積不為零的三角形&#xff0c;返回 0。 示例 1&#xff1a; 輸入&#xff1a;[2,1,2] 輸出&#xff1a;5 示例 2&…

《一天聊一個設計模式》備忘錄

備忘錄&#xff08;Memento&#xff09; Intent 在不違反封裝的情況下獲得對象的內部狀態&#xff0c;從而在需要時可以將對象恢復到最初狀態。 Class Diagram Originator&#xff1a;原始對象Caretaker&#xff1a;負責保存好備忘錄Memento&#xff1a;備忘錄&#xff0c;…

《一天聊一個設計模式》 策略

9. 策略&#xff08;Strategy&#xff09; Intent 定義一系列算法&#xff0c;封裝每個算法&#xff0c;并使它們可以互換。 策略模式可以讓算法獨立于使用它的客戶端。 Class Diagram Strategy 接口定義了一個算法族&#xff0c;它們都實現了 behavior() 方法。Context 是…

如何在eclipse jee中創建Maven project并且轉換為Dynamic web project

轉自&#xff1a;http://www.javaniu.com/maven-jee-dynamic-web-project.htm 注意:該文檔只針對以下eclipse版本&#xff0c;如圖 一.在eclipse的官方站點下載eclipse jee版本,地址http://www.eclipse.org/downloads/download.php?file/technology/epp/downloads/release/ind…

《一天聊一個設計模式》 抽象工廠

4. 抽象工廠&#xff08;Abstract Factory&#xff09; Intent 提供一個接口&#xff0c;用于創建 相關的對象家族 。 Class Diagram 抽象工廠模式創建的是對象家族&#xff0c;也就是很多對象而不是一個對象&#xff0c;并且這些對象是相關的&#xff0c;也就是說必須一起…

leetcode1047. 刪除字符串中的所有相鄰重復項(棧的日常應用)

給出由小寫字母組成的字符串 S&#xff0c;重復項刪除操作會選擇兩個相鄰且相同的字母&#xff0c;并刪除它們。 在 S 上反復執行重復項刪除操作&#xff0c;直到無法繼續刪除。 在完成所有重復項刪除操作后返回最終的字符串。答案保證唯一。 示例&#xff1a; 輸入&#xf…

算法題的輸入大總結

趕緊收藏吧&#xff0c;小白必備知識了 本文以求和為例 多組輸入&#xff0c;每組輸入共一行&#xff0c;包括兩個整數A, B Sample Input 1 2 12 24 400 500 Sample Output 3 36 900 import java.util.Scanner; public class Main {public static void main(String[] args) …

精華Java問題總結

當時在網上匯總了不知多少面試和基礎題&#xff0c;弄了個精華總結。 1、一個".java"源文件中是否可以包括多個類&#xff08;不是內部類&#xff09;&#xff1f;有什么限制&#xff1f; 可以有多個類&#xff0c;但只能有一個public的類&#xff0c;并且public的類…

復習Java的精華總結

小白和老手都應該看看的總結 輸入 java.util.Scanner 是 Java5 的新特征&#xff0c;我們可以通過 Scanner 類來獲取用戶的輸入。 下面是創建 Scanner 對象的基本語法&#xff1a; Scanner s new Scanner(System.in); 使用方法如下&#xff1a; //對應類型用對應的方法接…