如果寫劣質(zhì)代碼是犯罪,那我該判無期


?? 導(dǎo)讀
程序員痛恨遇到質(zhì)量低劣的代碼,但在高壓環(huán)境下,我們常為了最快解決當(dāng)下需求而忽略代碼規(guī)范,在無意識中堆積大量債務(wù)。我們還觀察到許多開發(fā)者被迫加班的罪魁禍?zhǔn)妆闶菍懙托Тa、不重視代碼優(yōu)化。編程路上,欲速則不達(dá)。接下來,我將為各位列舉9種我個(gè)人工作中高頻遇到的不整潔代碼行為,并提出針對性優(yōu)化建議。繼續(xù)閱讀~?? 目錄
1 代碼風(fēng)格和可讀性2 注釋 3?錯(cuò)誤處理和異常處理 4?代碼復(fù)用和模塊化 5?硬編碼 6?測試和調(diào)試 7 性能優(yōu)化 8 代碼安全性 9 版本控制和協(xié)作 10 總結(jié)
01
代碼風(fēng)格和可讀性
- 錯(cuò)誤習(xí)慣
|
不一致的命名規(guī)則
:
使用多種命名規(guī)則,如 camelCase、snake_case 和 PascalCase 等。過長的函數(shù)和方法:編寫過長的函數(shù)和方法,導(dǎo)致代碼難以閱讀和理解。
過長的行 :編寫超過50字符的代碼行,導(dǎo)致代碼難以閱讀。 |
? ? ? 1.1?變量命名不規(guī)范
在編程中,變量命名是非常重要的,良好的變量命名能夠提高代碼的可讀性和可維護(hù)性。不規(guī)范的命名會增加理解難度,以下是一個(gè)不規(guī)范命名的例子:
int?a, b, c; // 不具有描述性的變量名
float?f; // 不清楚變量表示的含義
這樣的變量命名不僅會降低代碼的可讀性,還可能會導(dǎo)致變量混淆,增加代碼維護(hù)的難度。正確的做法應(yīng)該使用有意義的名稱來命名變量。例如:
int?num1, num2, result; // 具有描述性的變量名
float?price; // 清晰明了的變量名
? ? ? 1.2?長函數(shù)和復(fù)雜邏輯
長函數(shù)和復(fù)雜邏輯是另一個(gè)常見的錯(cuò)誤和壞習(xí)慣。長函數(shù)難以理解和維護(hù),而復(fù)雜邏輯可能導(dǎo)致錯(cuò)誤和難以調(diào)試。以下是一個(gè)長函數(shù)和復(fù)雜邏輯的案例:
def?count_grade(score):
????if?score >= 90:
????????grade = 'A'
????elif?score >= 80:
????????grade = 'B'
????elif?score >= 70:
????????grade = 'C'
????elif?score >= 60:
????????grade = 'D'
????else:
????????grade = 'F'
????
????if?grade == 'A'?or?grade == 'B':
????????result = 'Pass'
????else:
????????result = 'Fail'
????return?result
在這個(gè)例子中,函數(shù) count_grade 包含了較長的邏輯和多個(gè)嵌套的條件語句,使得代碼難以理解和維護(hù)。正確的做法是將邏輯拆分為多個(gè)小函數(shù),每個(gè)函數(shù)只負(fù)責(zé)一個(gè)簡單的任務(wù),例如:
def?count_grade(score):
????grade = get_grade(score)
????result = pass_or_fail(grade)
????return?result
def?get_grade(score):
????if?score >= 90:
????????return?'A'
????elif?score >= 80:
????????return?'B'
????elif?score >= 70:
????????return?'C'
????elif?score >= 60:
????????return?'D'
????else:
????????return?'F'
def?pass_or_fail(grade):
????if?grade == 'A'?or?grade == 'B':
????????return?'Pass'
????else:
????????return?'Fail'
通過拆分函數(shù),我們使得代碼更加可讀和可維護(hù)。
? ? ? 1.3?過長的行
代碼行過長,會導(dǎo)致代碼難以閱讀和理解,增加了維護(hù)和調(diào)試的難度。例如:
def?f(x):
????if?x>0:return?'positive'?elif?x<0:return?'negative'else:return?'zero'
這段代碼的問題在于,它沒有正確地使用空格和換行,使得代碼看起來混亂,難以閱讀。正確的方法是,我們應(yīng)該遵循一定的代碼規(guī)范和風(fēng)格,使得代碼清晰、易讀。下面是按照 PEP 8規(guī)范改寫的代碼:
def?check_number(x):
????if?x > 0:
????????return?'positive'
????elif?x < 0:
????????return?'negative'
????else:
????????return?'zero'
這段代碼使用了正確的空格和換行,使得代碼清晰、易讀。
02
注釋
- 錯(cuò)誤習(xí)慣
| 缺少注釋: 沒有為代碼編寫注釋,導(dǎo)致其他人難以理解代碼的功能和邏輯。 過時(shí)的注釋:未及時(shí)更新注釋,使注釋與實(shí)際代碼不一致。 錯(cuò)誤注釋: 注釋上并不規(guī)范,常常使用一些不合理的注釋。 |
-
錯(cuò)誤的 注釋
注釋是非常重要的,良好的注釋可以提高代碼的可讀性和可維護(hù)性。以下是一個(gè)不規(guī)范的例子:
int?num1, num2; // 定義兩個(gè)變量
上述代碼中,注釋并沒有提供有用的信息,反而增加了代碼的復(fù)雜度。
03
錯(cuò)誤處理和異常處理
-
錯(cuò)誤的習(xí)慣
| 忽略錯(cuò)誤: 未對可能出現(xiàn)的錯(cuò)誤進(jìn)行處理。 過度使用異常處理: 濫用 try...except 結(jié)構(gòu),導(dǎo)致代碼邏輯混亂。 捕獲過于寬泛的異常: 捕獲過于寬泛的異常,如 except Exception,導(dǎo)致難以定位問題。 |
? ? ? 3.1?忽略錯(cuò)誤
我們往往會遇到各種錯(cuò)誤和異常。如果我們忽視了錯(cuò)誤處理,那么當(dāng)錯(cuò)誤發(fā)生時(shí),程序可能會崩潰,或者出現(xiàn)不可預(yù)知的行為。例如:
def?divide(x, y):
????return?x / y
這段代碼的問題在于,當(dāng) y 為0時(shí),它會拋出 ZeroDivisionError 異常,但是這段代碼沒有處理這個(gè)異常。下面是改進(jìn)的代碼:
def?divide(x, y):
????try:
????????return?x / y
????except?ZeroDivisionError:
????????return?'Cannot divide by zero!'
? ? ? 3.2?過度使用異常處理
我們可能會使用異常處理來替代條件判斷,這是不合適的。異常處理應(yīng)該用于處理異常情況,而不是正常的控制流程。例如:
def?divide(a, b):
????try:
????????result = a / b
????except?ZeroDivisionError:
????????result = float('inf')
????return?result
在這個(gè)示例中,我們使用異常處理來處理除以零的情況。正確做法:
def?divide(a, b):
????if?b == 0:
????????result = float('inf')
????else:
????????result = a / b
????return?result
在這個(gè)示例中,我們使用條件判斷來處理除以零的情況,而不是使用異常處理。
? ? ? 3.3?捕獲過于寬泛的異常
捕獲過于寬泛的異??赡軐?dǎo)致程序崩潰或隱藏潛在的問題。以下是一個(gè)案例:
try?{
????// 執(zhí)行一些可能拋出異常的代碼
} catch?(Exception?e) {
????// 捕獲所有異常,并忽略錯(cuò)誤
}
在這個(gè)例子中,異常被捕獲后,沒有進(jìn)行任何處理或記錄,導(dǎo)致程序無法正確處理異常情況。正確的做法是根據(jù)具體情況,選擇合適的異常處理方式,例如:
try?{
????// 執(zhí)行一些可能拋出異常的代碼
} catch?(FileNotFoundException e) {
????// 處理文件未找到異常
????logger.error("File not found", e);
} catch?(IOException e) {
????// 處理IO異常
????logger.error("IO error", e);
} catch?(Exception e) {
????// 處理其他異常
????logger.error("Unexpected error", e);
}
通過合理的異常處理,我們可以更好地處理異常情況,增加程序的穩(wěn)定性和可靠性。
04
錯(cuò)誤處理和異常處理
- 錯(cuò)誤的習(xí)慣
| 缺乏復(fù)用性: 代碼冗余,維護(hù)困難,增加 bug 出現(xiàn)的可能性。 缺乏模塊化: 代碼耦合度高,難以重構(gòu)和測試。 |
? ? ? 4.1?缺乏復(fù)用性
代碼重復(fù)是一種非常常見的錯(cuò)誤。當(dāng)我們需要實(shí)現(xiàn)某個(gè)功能時(shí),可能會復(fù)制粘貼之前的代碼來實(shí)現(xiàn),這樣可能會導(dǎo)致代碼重復(fù),增加代碼維護(hù)的難度。例如:
???def?calculate_area_of_rectangle(length, width):
???????return?length * width
???def?calculate_volume_of_cuboid(length, width, height):
???????return?length * width * height
???def?calculate_area_of_triangle(base, height):
???????return?0.5?* base * height
???def?calculate_volume_of_cone(radius, height):
???????return?(1/3) * 3.14?* radius * radius * height
上述代碼中,計(jì)算邏輯存在重復(fù),這樣的代碼重復(fù)會影響代碼的可維護(hù)性。為了避免代碼重復(fù),我們可以將相同的代碼復(fù)用,封裝成一個(gè)函數(shù)或者方法。例如:
???def?calculate_area_of_rectangle(length, width):
???????return?length * width
???def?calculate_volume(length, width, height):
???????return?calculate_area_of_rectangle(length, width) * height
???def?calculate_area_of_triangle(base, height):
???????return?0.5?* base * height
???def?calculate_volume_of_cone(radius, height):
???????return?(1/3) * 3.14?* radius * radius * height
這樣,我們就可以避免代碼重復(fù),提高代碼的可維護(hù)性。
? ? ? 4.2?缺乏模塊化
缺乏模塊化是一種常見的錯(cuò)誤,這樣容易造成冗余,降低代碼的可維護(hù)性,例如:
???class?User:
???????def?__init__(self, name):
???????????self.name = name
???????def?save(self):
???????????# 保存用戶到數(shù)據(jù)庫的邏輯
???????def?send_email(self, content):
???????????# 發(fā)送郵件的邏輯
???class?Order:
???????def?__init__(self, user, product):
???????????self.user = user
???????????self.product = product
???????def?save(self):
???????????# 保存訂單到數(shù)據(jù)庫的邏輯
???????def?send_email(self, content):
???????????# 發(fā)送郵件的邏輯
???```
此例中,User 和 Order 類都包含了保存和發(fā)送郵件的邏輯,導(dǎo)致代碼重復(fù),耦合度高。我們可以通過將發(fā)送郵件的邏輯提取為一個(gè)獨(dú)立的類,例如:
???class?User:
???????def?__init__(self, name):
???????????self.name = name
???????def?save(self):
???????????# 保存用戶到數(shù)據(jù)庫的邏輯
???class?Order:
???????def?__init__(self, user, product):
???????????self.user = user
???????????self.product = product
???????def?save(self):
???????????# 保存訂單到數(shù)據(jù)庫的邏輯
???class?EmailSender:
???????def?send_email(self, content):
???????????# 發(fā)送郵件的邏輯
通過把發(fā)送郵件單獨(dú)提取出來,實(shí)現(xiàn)了模塊化?,F(xiàn)在 User 和 Order 類只負(fù)責(zé)自己的核心功能,而發(fā)送郵件的邏輯由 EmailSender 類負(fù)責(zé)。這樣一來,代碼更加清晰,耦合度降低,易于重構(gòu)和測試。
05
硬編碼
- 錯(cuò)誤的習(xí)慣
| 常量: 設(shè)置固定常量,導(dǎo)致維護(hù)困難。 全局變量: 過度使用全局變量,導(dǎo)致程序的狀態(tài)難以跟蹤。 |
? ? ? 5.1?常量
在編程中,我們經(jīng)常需要使用一些常量,如數(shù)字、字符串等。然而,直接在代碼中硬編碼這些常量是一個(gè)不好的習(xí)慣,因?yàn)樗鼈兛赡軙谖磥戆l(fā)生變化,導(dǎo)致維護(hù)困難。例如:
def?calculate_score(score):
????if?(score > 60) {
????//?do?something
}
這里的60就是一個(gè)硬編碼的常量,導(dǎo)致后續(xù)維護(hù)困難,正確的做法應(yīng)該使用常量或者枚舉來表示。例如:
PASS_SCORE = 60;
def?calculate_score(score):
????if?(score > PASS_SCORE) {
????????//?do?something
????}
這樣,我們就可以避免硬編碼,提高代碼的可維護(hù)性。
? ? ? 5.2?全局變量
過度使用全局變量在全局范圍內(nèi)都可以訪問和修改。因此,過度使用全局變量可能會導(dǎo)致程序的狀態(tài)難以跟蹤,增加了程序出錯(cuò)的可能性。例如:
counter = 0
def?increment():
????global?counter
????counter += 1
這段代碼的問題在于,它使用了全局變量 counter,使得程序的狀態(tài)難以跟蹤。我們應(yīng)該盡量減少全局變量的使用,而是使用函數(shù)參數(shù)和返回值來傳遞數(shù)據(jù)。例如:
def?increment(counter):
????return?counter + 1
這段代碼沒有使用全局變量,而是使用函數(shù)參數(shù)和返回值來傳遞數(shù)據(jù),使得程序的狀態(tài)更易于跟蹤。
06
測試和調(diào)試
- 錯(cuò)誤的習(xí)慣
| 單元測試: 不進(jìn)行單元測試會導(dǎo)致無法及時(shí)發(fā)現(xiàn)和修復(fù)代碼中的錯(cuò)誤,增加代碼的不穩(wěn)定性和可維護(hù)性。 邊界測試: 不進(jìn)行邊界測試可能導(dǎo)致代碼在邊界情況下出現(xiàn)錯(cuò)誤或異常。 代碼的可測試性: 有些情況依賴于當(dāng)前條件,使測試變得很難。 |
? ? ? 6.1?單元測試
單元測試是驗(yàn)證代碼中最小可測試單元的方法,下面是不添加單元測試的案例:
def?add_number(a, b):
????return?a + b
在這個(gè)示例中,我們沒有進(jìn)行單元測試來驗(yàn)證函數(shù) `add_number` 的正確性。正確示例:
import?unittest
def?add_number(a, b):
????return?a + b
class?TestAdd(unittest.TestCase):
????def?add_number(self):
????????self.assertEqual(add(2, 3), 5)
if?__name__ == '__main__':
????unittest.main()
在這個(gè)示例中,我們使用了 `unittest` 模塊進(jìn)行單元測試,確保函數(shù) `add` 的正確性。
? ? ? 6.2?邊界測試
邊界測試是針對輸入的邊界條件進(jìn)行測試,以驗(yàn)證代碼在邊界情況下的行為下面是錯(cuò)誤示例:
def?is_even(n):
????return?n % 2?== 0
在這個(gè)示例中,我們沒有進(jìn)行邊界測試來驗(yàn)證函數(shù) `is_even` 在邊界情況下的行為。正確示例:
import unittest
def?is_even(n):
????return?n % 2?== 0
class?TestIsEven(unittest.TestCase):
????def?test_even(self):
????????self.assertTrue(is_even(2))
????????self.assertFalse(is_even(3))
if?__name__?== '__main__':
????unittest.main()
在這個(gè)示例中,我們使用了 `unittest` 模塊進(jìn)行邊界測試,驗(yàn)證函數(shù) `is_even` 在邊界情況下的行為。
? ? ? 6.3?可測試性
代碼的可測試性我們需要編寫測試來驗(yàn)證代碼的正確性。如果我們忽視了代碼的可測試性,那么編寫測試將會變得困難,甚至無法編寫測試。例如:
def?get_current_time():
????return?datetime.datetime.now()
這段代碼的問題在于,它依賴于當(dāng)前的時(shí)間,這使得我們無法編寫確定性的測試。我們應(yīng)該盡量減少代碼的依賴,使得代碼更易于測試。例如:
def?get_time(now):
????return?now
這段代碼不再依賴于當(dāng)前的時(shí)間,而是通過參數(shù)傳入時(shí)間,這使得我們可以編寫確定性的測試。
07
性能優(yōu)化
- 錯(cuò)誤的習(xí)慣
| 過度優(yōu)化: 過度優(yōu)化可能會導(dǎo)致代碼難以理解和維護(hù),甚至可能會引入新的錯(cuò)誤。 合適的數(shù)據(jù)結(jié)構(gòu): 選擇合適的數(shù)據(jù)結(jié)構(gòu)可以提高代碼的性能。 |
? ? ? 7.1?過度優(yōu)化
我們往往會試圖優(yōu)化代碼,使其運(yùn)行得更快。然而,過度優(yōu)化可能會導(dǎo)致代碼難以理解和維護(hù),甚至可能會引入新的錯(cuò)誤。例如:
def?sum(numbers):
????return?functools.reduce(operator.add, numbers)
這段代碼的問題在于,它使用了 functools.reduce 和 operator.add 來計(jì)算列表的和,雖然這樣做可以提高一點(diǎn)點(diǎn)性能,但是這使得代碼難以理解。我們應(yīng)該在保持代碼清晰和易讀的前提下,進(jìn)行適度的優(yōu)化。例如:
def?sum(numbers):
????return?sum(numbers)
這段代碼使用了內(nèi)置的 sum 函數(shù)來計(jì)算列表的和,雖然它可能比上面的代碼慢一點(diǎn),但是它更清晰、易讀。
? ? ? 7.2?沒有使用合適的數(shù)據(jù)結(jié)構(gòu)
選擇合適的數(shù)據(jù)結(jié)構(gòu)可以提高代碼的性能。使用不合適的數(shù)據(jù)結(jié)構(gòu)可能導(dǎo)致代碼執(zhí)行緩慢或占用過多的內(nèi)存。例如:
def?find_duplicate(numbers):
????duplicates = []
????for?i in?range(len(numbers)):
????????if?numbers[i] in?numbers[i+1:]:
????????????duplicates.append(numbers[i])
????return?duplicates
在這個(gè)示例中,我們使用了列表來查找重復(fù)元素,但這種方法的時(shí)間復(fù)雜度較高。我們可以使用集合來查找元素。例如:
def find_duplicate(numbers):
????duplicates = set()
????seen = set()
????for?num?in?numbers:
????????if?num?in?seen:
????????????duplicates.add(num)
????????else:
????????????seen.add(num)
????return?list(duplicates)
我們使用了集合來查找重復(fù)元素,這種方法的時(shí)間復(fù)雜度較低。
08
代碼安全性
- 錯(cuò)誤的習(xí)慣
| 輸入驗(yàn)證: 不正確的輸入驗(yàn)證可能導(dǎo)致安全漏洞,如 SQL 注入、跨站腳本攻擊等。 密碼存儲: 不正確的密碼存儲可能導(dǎo)致用戶密碼泄露。 權(quán)限控制: 不正確的權(quán)限控制可能導(dǎo)致未經(jīng)授權(quán)的用戶訪問敏感信息或執(zhí)行特權(quán)操作。 |
? ? ? 8.1?輸入驗(yàn)證
沒有對用戶輸入進(jìn)行充分驗(yàn)證和過濾可能導(dǎo)致惡意用戶執(zhí)行惡意代碼或獲取敏感信息。例如:
import sqlite3
def get_user(username):
????conn = sqlite3.connect('database.db')
????cursor?= conn.cursor()
????query = f"SELECT * FROM users WHERE username = '{username}'"
????cursor.execute(query)
????user = cursor.fetchone()
????conn.close()
????return?user
在這個(gè)示例中,我們沒有對用戶輸入的 `username` 參數(shù)進(jìn)行驗(yàn)證和過濾,可能導(dǎo)致 SQL 注入攻擊。正確示例:
import sqlite3
def get_user(username):
????conn = sqlite3.connect('database.db')
????cursor?= conn.cursor()
????query = "SELECT * FROM users WHERE username = ?"
????cursor.execute(query, (username,))
????user = cursor.fetchone()
????conn.close()
????return?user
在這個(gè)示例中,我們使用參數(shù)化查詢來過濾用戶輸入,避免了 SQL 注入攻擊。
? ? ? 8.2?不正確的密碼存儲
將明文密碼存儲在數(shù)據(jù)庫或文件中,或使用不安全的哈希算法存儲密碼都是不安全的做法。錯(cuò)誤示例:
import?hashlib
def?store_password(password):
????hashed_password = hashlib.md5(password.encode()).hexdigest()
????# 存儲 hashed_password 到數(shù)據(jù)庫或文件中
在這個(gè)示例中,我們使用了不安全的哈希算法 MD5 來存儲密碼。正確示例:
import?hashlib
import?bcrypt
def?store_password(password):
????hashed_password = bcrypt.hashpw(password.encode(), bcrypt.gensalt())
????# 存儲 hashed_password 到數(shù)據(jù)庫或文件中
在這個(gè)示例中,我們使用了更安全的哈希算法 bcrypt 來存儲密碼。
? ? ? 8.3?不正確的權(quán)限控制
沒有正確驗(yàn)證用戶的身份和權(quán)限可能導(dǎo)致安全漏洞。錯(cuò)誤示例:
def?delete_user(user_id):
????if?current_user.is_admin:
????????# 執(zhí)行刪除用戶的操作
????else:
????????raise?PermissionError("You don't have permission to delete users.")
在這個(gè)示例中,我們只檢查了當(dāng)前用戶是否為管理員,但沒有進(jìn)行足夠的身份驗(yàn)證和權(quán)限驗(yàn)證。正確示例:
def?delete_user(user_id):
????if?current_user.is_authenticated and?current_user.is_admin:
????????# 執(zhí)行刪除用戶的操作
????else:
????????raise?PermissionError("You don't have permission to delete users.")
在這個(gè)示例中,我們不僅檢查了當(dāng)前用戶是否為管理員,還檢查了當(dāng)前用戶是否已經(jīng)通過身份驗(yàn)證。
09
版本控制和協(xié)作
-
錯(cuò)誤的習(xí)慣
| 版本提交信息: 不合理的版本提交信息會造成開發(fā)人員難以理解和追蹤代碼的變化。 忽略版本控制和備份: 沒有備份代碼和版本控制的文件可能導(dǎo)致丟失代碼、難以追溯錯(cuò)誤來源和無法回滾等問題。 |
? ? ? 9.1?版本提交信息
不合理的版本提交信息可能導(dǎo)致代碼丟失、開發(fā)人員難以理解等問題。錯(cuò)誤示例:
git commit?-m "Fixed a bug"
在這個(gè)例子中,提交信息沒有提供足夠的上下文和詳細(xì)信息,導(dǎo)致其他開發(fā)人員難以理解和追蹤代碼的變化。正確的做法是提供有意義的提交信息,例如:
$?git commit -m "Fixed a bug in calculate function, which caused grade calculation for scores below 60"
通過提供有意義的提交信息,我們可以更好地追蹤代碼的變化,幫助其他開發(fā)人員理解和維護(hù)代碼。
? ? ? 9.2?忽略版本控制和備份
忽略使用版本控制工具進(jìn)行代碼管理和備份是一個(gè)常見的錯(cuò)誤。錯(cuò)誤示例:
$?mv important_code.py important_code_backup.py
$?rm important_code.py
在這個(gè)示例中,開發(fā)者沒有使用版本控制工具,只是簡單地對文件進(jìn)行重命名和刪除,沒有進(jìn)行適當(dāng)?shù)膫浞莺陀涗洝U_示例:
$?git clone?project.git
$?cp important_code.py important_code_backup.py
$?git add .
$?git commit -m "Created backup of important code"
$?git push origin master
$?rm important_code.py
在這個(gè)示例中,開發(fā)者使用了版本控制工具進(jìn)行代碼管理,并在刪除之前創(chuàng)建了備份,確保了代碼的安全性和可追溯性。
10
總結(jié)
好的代碼應(yīng)該如同一首好文,讓人愛不釋手。優(yōu)雅的代碼,不僅是功能完善,更要做好每一個(gè)細(xì)節(jié)。
最后,引用韓磊老師在《代碼整潔之道》寫到的一句話送給大家:
“
細(xì)節(jié)之中自有天地,整潔成就卓越代碼。
-End- 原創(chuàng)作者|孔垂航 技術(shù)責(zé)編|劉銀松

寫代碼時(shí)犯過什么搞笑的低級錯(cuò)誤?你又有什么避免錯(cuò)誤的好辦法?歡迎在評論區(qū)分享討論。我們將選取1則最有意義的分享,送出 騰訊云開發(fā)者-手腕墊 1個(gè)(見下圖)。7月24日中午12點(diǎn)開獎。

關(guān)注并星標(biāo)騰訊云開發(fā)者
第一時(shí)間看鵝廠技術(shù)

