返回文章列表

提升程式碼可讀性實務

本文探討如何提升程式碼可讀性,涵蓋程式碼格式、外部檔案、小型程式碼慣例、多重陳述句處理、清晰程式碼的撰寫、逐步精煉、由頂向下設計、程式碼重構、增量式開發、自我說明程式碼、註解的使用以及停止編碼的重要性等導向,提供實務技巧與範例說明,幫助開發者寫出更易理解和維護的程式碼。

軟體開發 程式設計

程式碼可讀性是軟體開發中重要的一環,影響著開發效率、維護成本和團隊協作。良好的程式碼可讀性不僅能讓程式更容易理解,還能減少錯誤發生的機率。提升程式碼可讀性的方法有很多,包含程式碼格式、註解、命名規範、程式碼結構等。本文將探討如何透過這些技巧,讓程式碼更清晰、更易懂,並以實際案例說明如何應用這些技巧。程式碼格式方面,應保持一致的縮排、換行和空格使用,並善用多行表示式來避免程式碼過於冗長。清晰的命名規範也很重要,變數和函式名稱應該具有描述性,能夠清楚地表達其用途。此外,適當的註解可以幫助理解程式碼的邏輯和意圖,但過多的註解反而會增加閱讀負擔,因此需要權衡註解的數量和必要性。

自我說明程式碼的重要性

撰寫易於理解的程式碼是軟體開發中的重要環節。良好的程式碼不僅能提高開發效率,還能減少維護成本並提升團隊協作能力。

程式碼格式與可讀性

在撰寫程式碼時,適當的格式安排能大幅提升可讀性。以下列舉幾種常見的寫法:

多行表示式的處理

# Method 1
netSalary = jobIncome + hobbyIncome + stockDividends + (rents - utilities) - personalLivingExpenses - mortgagePayments - medicalExpenses
print(netSalary)

# Method 2
netSalary = (jobIncome +
             hobbyIncome +
             stockDividends +
             (rents - utilities) -
             personalLivingExpenses -
             mortgagePayments -
             medicalExpenses)
print(netSalary)

# Method 3
netSalary = (jobIncome
             + hobbyIncome
             + stockDividends
             + (rents - utilities)
             - personalLivingExpenses
             - mortgagePayments
             - medicalExpenses)
print(netSalary)

內容解密:

  1. 多行表示式的優點:將長表示式拆分為多行可以提高可讀性,特別是在涉及多個運算元時。
  2. 運算元的位置:在程式碼中,通常在運算元前換行(Method 3),這樣可以清晰地看出接下來的運算。
  3. 一致性:選擇一種風格並在整個專案中保持一致,有助於團隊協作。

外部檔案的重要性

在程式碼頂部新增外部檔案,可以提供有關程式的重要資訊。以下是一個範例:

"""
+===============+=====-========*========-======+===========+
|| CIRCLE DETECTION ||
|| by M. Stueben (October 8, 2017) ||
|| Artificial Intelligence; Mr. Stueben, ||
|| Periods 1, 2, and 7 ||
|| ||
|| Description: This program detects a circle (radius ||
|| and center) in a 512x512 gray-scale ||
|| image of a circle and 500 random points ||
|| (aka snow, noise). ||
|| It then draws a new circle in red over the ||
|| initial circle. The circles almost match. ||
|| Algorithms: Gaussian smoothing, Sobel operator/filter, ||
|| Canny edge detection, and a vote accumulator- ||
|| matrix equal to the size of the image. ||
|| Downloads: None ||
|| Language: Python Ver. 3.3 ||
|| Graphics: Tkinter Graphics ||
+==========================================================+
"""

內容解密:

  1. 檔案結構:清晰的檔案結構有助於快速瞭解程式的功能和實作細節。
  2. 必要資訊:包含程式標題、作者、日期、課程資訊、使用的演算法和程式語言等。
  3. 註解規範:使用一致的註解風格,有助於提高程式碼的可讀性和維護性。

小型程式碼慣例

在日常程式設計中,一些小的慣例可以提高程式碼的可讀性。例如:

# 間距的使用
ANN = inputs, w, h, v  # 無間距
ANN = inputs, w, h, v  # 有間距

y = 2 * (x + y)  # 推薦寫法
y = 2*(x + y)    # 也可接受

# 三角形面積計算
def triangleArea(x1, y1, x2, y2, x3, y3):  # vertices
    return abs((x1-x3)*(y2-y3) - (x2-x3)*(y1-y3))/2

內容解密:

  1. 間距規範:適當的間距可以提高程式碼的可讀性,例如在運算元前後新增空格。
  2. 函式定義:函式引數之間的間距應保持一致。
  3. PEP 0008 指引:遵循 Python 的官方風格指引(PEP 0008),有助於撰寫規範化的程式碼。

多重陳述句的處理

在某些情況下,將多個陳述句放在同一行是可接受的,但須確保邏輯清晰。

# Method 1 (acceptable, but discouraged in Python)
a = 1; b = 2; c = 3; d = 4

# Method 2 (common in Python)
a, b, c, d = 1, 2, 3, 4

# Method 3 (bulky, but most readable)
a = 1
b = 2
c = 3
d = 4

內容解密:

  1. 不同寫法的比較:不同的寫法各有優缺點,需根據實際情況選擇最合適的方式。
  2. 可讀性優先:在保證程式正確性的前提下,應優先考慮可讀性。

清晰與意外的程式碼

有些程式碼雖然不符傳統寫法,但仍然清晰易懂。例如:

for x in dataSet:
    if -10 <= x < 0: print('Case I'); continue
    if 0 <= x < 10: print('Case II'); continue
    if 10 <= x < 20: print('Case III'); continue
    print(x)

內容解密:

  1. 垂直對齊:適當的垂直對齊可以提高程式碼的可讀性。
  2. 清單解析:雖然某些寫法可能被視為不規範,但只要清晰易懂,仍然是可接受的。

綜上所述,撰寫自我說明的程式碼需要綜合考慮格式、檔案、命名和邏輯結構等多個方面。遵循一致的程式設計規範和風格指引,可以有效提高程式碼的可讀性和維護性。

逐步精煉:提升程式碼可讀性的關鍵

在軟體開發的過程中,如何撰寫出易於理解和維護的程式碼一直是開發者關注的重點。Rudolf Flesch 在其著作《The Art of Readable Writing》中提到,只有故事才是真正可讀的。這句話對於程式設計同樣適用,因為好的程式碼應該像在講一個清晰的故事,讓讀者能夠輕鬆理解程式的功能和設計意圖。

自我描述的程式碼與逐步精煉

自我描述的程式碼(Self-Documenting Code)是一種能夠透過其結構和命名清晰地表達其功能和邏輯的程式碼。實作這種程式碼的一種有效方法是使用逐步精煉(Step-Wise Refinement),這是一種由頂向下的設計方法,也被稱為結構化程式設計。

逐步精煉的核心概念

  1. 主函式包含具有描述性的函式呼叫:主函式中包含一系列具有描述性的函式呼叫,例如 enterData()computeData()printData()。這些函式呼叫形成了一個大綱,概述了程式的主要步驟和功能。

    def main():
        matrix = createSudoku()
        matrix = solveTheSudoku(matrix)
        printVerification(matrix)
        root.mainloop()  # Required for Tk graphics.
    
  2. 子函式進一步細化:每個被呼叫的函式可能進一步包含更多的子函式呼叫,這些子函式同樣具有描述性的名稱。這種層層遞進的結構使得程式的邏輯變得清晰,易於理解。

  3. 最終實作具體的電腦指令:雖然逐步精煉鼓勵使用抽象的函式呼叫來描述程式邏輯,但最終還是需要實作具體的電腦指令。這種方法的目標是透過選擇具有描述性的函式名稱,使得讀者能夠在不閱讀大量程式碼或註解的情況下理解程式的設計。

由頂向下與由底向上的設計

  • 由頂向下的設計:這種方法從整體出發,將大問題分解為小問題,每一步都盡可能地清晰和簡單。它有助於保持程式碼的結構清晰,易於理解和維護。

  • 由底向上的設計:這種方法則是從具體的實作細節開始,逐步構建出整個系統。它類別似於一種「流意識」的程式設計方式,適合於小型程式的開發。

無論採用哪種設計方法,重要的是讓程式碼能夠以由頂向下的方式被閱讀,這樣可以方便在不同層次上進行程式驗證,並且使審閱者(甚至是作者自己在未來某個時候)更容易理解程式。

例項分析

以下是一個實際的例子,展示瞭如何透過逐步精煉來改進 main 函式:

原始版本:

def main():
    image = list(readPixelColorsFromImageFile(IMAGE_FILE_NAME='e:\\lena_rgb_p3.ppm'))
    displayImageInWindow(image, False)
    saveTheImageGrayScaleNumbersToFile(image, GRAY_SCALE_NUMBERS_FILE_NAME='e:\\grayScale.ppm')
    image = extractTheImageGrayScaleNumbersFromFile(GRAY_SCALE_NUMBERS_FILE_NAME='e:\\grayScale.ppm')
    displayImageInWindow(image, False)
    image = smoothTheImage(image, NUMBER_OF_TIMES_TO_SMOOTH_IMAGE=4)
    saveTheImageGrayScaleNumbersToFile(image, GRAY_SCALE_NUMBERS_FILE_NAME='e:\\smoothed.ppm')
    # ... 其他操作

改進後的版本:

def main():
    imageFileName = 'g:\\lena_rgb_p3.ppm'
    grayScaleNumbersFileName = convertColorFileToGrayScaleFile(imageFileName)
    smoothedFileName = extractSmoothAndSaveImage(grayScaleNumbersFileName)
    # ... 繼續其他步驟

內容解密:

  1. 抽取主要步驟:改進後的 main 函式透過呼叫具有描述性的函式(如 convertColorFileToGrayScaleFileextractSmoothAndSaveImage),使得整個處理流程一目瞭然。
  2. 簡化主函式邏輯:原始版本中的 main 函式過於冗長,包含多個步驟的實作細節。改進後的版本將這些細節封裝到不同的函式中,使得 main 函式變得更加簡潔。
  3. 提高可讀性和可維護性:透過逐步精煉,程式碼變得更容易理解和維護。如果需要修改某個步驟的實作細節,可以直接定位到對應的函式進行修改,而不會影響到其他部分。

程式碼重構與註解的藝術

在軟體開發過程中,程式碼的可讀性和可維護性是至關重要的。良好的程式碼結構和適當的註解可以大大提高程式的可理解性,減少維護成本。本文將探討如何透過重構和註解來提升程式碼的品質。

增量式開發

增量式開發(Incremental Development)是一種軟體開發方法論,主張透過逐步迭代來完成專案。首先實作一個簡化的版本,接著在這個基礎上不斷新增功能,直到完成最終版本。這種方法的好處在於能夠持續獲得一個可執行的程式,從而減少專案後期的壓力和不確定性。

增量式開發的優點

  • 能夠持續獲得一個可執行的程式,給予開發者信心和成就感。
  • 減少專案後期的壓力和不確定性。
  • 早期獲得使用者反饋,有助於改善程式的使用者和介面設計。

自我說明程式碼

自我說明程式碼(Self-documenting Code)是指透過程式碼本身就能清晰地表達其功能和邏輯,無需過多的註解。這種程式碼風格有助於減少註解的數量,使程式更簡潔易讀。

範例:檢查字串是否全為母音

def is_all_vowels(stng):
    for ch in stng.lower():
        if ch not in ['a', 'e', 'i', 'o', 'u']:
            return False
    return True

內容解密:

  1. is_all_vowels 函式檢查輸入字串 stng 是否全部由母音組成。
  2. 將輸入字串轉換為小寫,以確保檢查不區分大小寫。
  3. 遍歷字串中的每個字元,如果發現非母音字元,立即傳回 False
  4. 若遍歷完成後未發現非母音字元,則傳回 True

註解的使用

雖然自我說明程式碼可以減少對註解的依賴,但在某些情況下,適當的註解仍然是必要的。註解可以用來解釋複雜的邏輯、提供背景資訊或標明程式碼的組織結構。

範例:輸出八皇后棋盤

def print_board(board):  # Example: board = [3,5,7,2,0,6,4,1]
    print("###################")
    for col in board:
        s = ['- '] * len(board)  # build a list of strings with no 'Q '
        s[col] = 'Q '  # insert 'Q 's in the correct places
        print('# ' + ''.join(s) + "#")  # make the list into one string.
    print("###################")

內容解密:

  1. print_board 函式根據輸入的 board 清單輸出一個代表八皇后問題解法的棋盤。
  2. board 清單中的每個元素代表皇后在對應列中的位置。
  3. 使用迴圈遍歷 board,為每一列建立一個包含 ‘Q’ 和 ‘-’ 的字串,以表示皇后的位置和其他格子。
  4. 將建立好的字串輸出,形成棋盤的視覺表示。

撰寫有效的註解

有效的註解應該清晰、簡潔,並提供額外的資訊。註解不應該重複程式碼已經表達的內容,而應該用於解釋為什麼要這樣做,或提供額外的背景資訊。

原則

  • 使用完整的句子撰寫註解,以提高可讀性。
  • 將註解與相關程式碼放在一起,以便於理解。
  • 使用註解來解釋複雜的邏輯或演算法。
  • 提供範例以幫助理解函式或類別的使用方法。

停止編碼,理解後再繼續

在編寫程式碼時,遇到疑惑或不確定的情況,應立即停止編碼,待理解清楚後再繼續。這是因為編寫程式碼不僅是寫出能夠執行的程式碼,更重要的是寫出可維護、可理解的程式碼。

為什麼需要停止編碼?

當你開始感到困惑時,代表你可能對程式碼的某些部分尚未完全理解。這時繼續編碼可能會導致更多問題的堆積,使程式碼變得更加難以維護和除錯。

如何實踐停止編碼?

  1. 理解需求:在開始編碼前,確保你完全理解了需求和任務目標。
  2. 設計和規劃:花時間設計和規劃你的程式碼結構和邏輯。
  3. 逐步實作:將大任務分解為小任務,逐步實作和測試。
  4. 暫停和反思:遇到疑惑時,暫停編碼,反思和研究相關知識或尋求幫助。

例子:旅行商問題

在處理像旅行商問題(TSP)這樣複雜的問題時,正確的資料結構選擇至關重要。TSP的資料可以用一個包含xy座標的列表來表示。建議使用列表的列表,而不是元組的列表,並且在每個元素的起始位置新增一個id。例如:

city = [[id, x-value, y-value], [id, x-value, y-value], ..., [id, x-value, y-value]]

內容解密:

  • 使用列表而非元組是因為列表是可變的,可以在後續過程中修改其內容。
  • 新增id是為了可能在後續過程中給予每個xy座標某種屬性,如存取狀態等。

註解和檔案字串的重要性

雖然有人認為註解是多餘的,但適當的註解和檔案字串對於程式碼的可讀性和可維護性至關重要。特別是在處理複雜邏輯或遞迴函式時,註解可以幫助開發者理解程式碼的意圖。