跳轉到內容

軟體工程/實現/程式碼約定簡介

來自華夏公益教科書,開放的書籍,開放的世界

編碼約定是一組針對特定程式語言的指南,建議針對用這種語言編寫的程式片段的每個方面,推薦程式設計風格、實踐和方法。這些約定通常涵蓋檔案組織、縮排、註釋、宣告、語句、空白、命名約定、程式設計實踐、程式設計原則、程式設計經驗法則等。強烈建議軟體程式設計師遵循這些指南,以幫助提高其原始碼的可讀性,並使軟體維護更輕鬆。編碼約定僅適用於軟體專案的維護人員和同行評審人員。約定可以在整個團隊或公司遵循的一套文件化的規則中正式化,也可以像個人習慣的編碼實踐一樣非正式。編碼約定不受編譯器強制執行。因此,不遵循某些或所有規則不會影響從原始碼建立的可執行程式。程式碼編制對使用計算機非常有幫助

軟體維護

[編輯 | 編輯原始碼]

降低軟體維護成本是遵循編碼約定的最常被引用的原因。在他們關於 Java 程式語言的程式碼約定的介紹中,Sun Microsystems 提供了以下理由:[1]

程式碼約定對程式設計師來說很重要,原因有很多

  • 80% 的軟體生命週期成本用於維護。
  • 幾乎沒有軟體是由最初的作者維護其整個生命週期的。
  • 程式碼約定提高了軟體的可讀性,使工程師能夠更快、更徹底地理解新程式碼。
  • 如果您將原始碼作為產品交付,您需要確保其包裝和清潔程度與您建立的任何其他產品一樣好。

軟體同行評審經常涉及閱讀原始碼。這種型別的同行評審主要是缺陷檢測活動。根據定義,只有程式碼的最初作者在程式碼提交評審之前閱讀過原始檔。使用一致指南編寫的程式碼更容易讓其他評審人員理解和吸收,從而提高缺陷檢測過程的效率。

即使對於最初的作者來說,一致的編碼軟體也簡化了可維護性。不能保證個人會記住很久以前編寫的一段特定程式碼以特定方式編寫的確切理由。編碼約定可以提供幫助。一致使用空白會提高可讀性,並縮短理解軟體所需的時間。

重構是指一種軟體維護活動,其中修改原始碼以提高可讀性或改進其結構。通常會對軟體進行重構,使其在初始釋出後符合團隊的既定編碼標準。任何不改變軟體行為的更改都可以被認為是重構。常見的重構活動包括更改變數名、重新命名方法、移動方法或整個類以及將大型方法(或函式)分解成更小的方法。

敏捷軟體開發方法規劃了定期(甚至持續)重構,使其成為團隊軟體開發過程的一個組成部分。[2]

任務自動化

[編輯 | 編輯原始碼]

編碼約定允許使用簡單的指令碼或程式,這些指令碼或程式的任務是處理原始碼,以實現除將其編譯為可執行檔案之外的其他目的。計算軟體大小(原始碼行數)以跟蹤當前專案進度或為將來的專案估算建立基線是常見的做法。

一致的編碼標準反過來可以使測量結果更加一致。原始碼註釋中的特殊標記通常用於處理文件,javadoc 和 doxygen 是兩個著名的例子。該工具指定了一組標記的使用,但它們在專案中的使用是透過約定決定的。

編碼約定簡化了編寫處理現有軟體的新軟體。自 20 世紀 50 年代以來,靜態程式碼分析的使用一直在不斷增長。這種開發工具的增長部分源於從業人員自身的成熟度和複雜性(以及現代對安全性的關注),但也源於語言本身的性質。

語言因素

[編輯 | 編輯原始碼]

所有軟體從業人員都必須解決組織和管理大量詳細指令的問題,這些指令最終將被處理以執行其編寫任務。對於除最小的軟體專案之外的所有專案,原始碼(指令)都將被劃分為單獨的檔案,並且經常在多個目錄中。程式設計師自然地將密切相關的函式(行為)收集到同一個檔案中,並將相關的檔案收集到目錄中。隨著軟體開發從純粹的程式化程式設計(如 FORTRAN 中的程式設計)發展到面向物件結構(如 C++ 中的程式設計),編寫單個(公共)類的程式碼在單個檔案(“每個檔案一個類”約定)中成為一種慣例。[3][4] Java 更進一步 - 如果 Java 編譯器在檔案中發現多個公共類,則會返回錯誤。

一種語言中的約定可能是另一種語言中的要求。語言約定也會影響單個原始檔。用於處理原始碼的每個編譯器(或直譯器)都是唯一的。編譯器應用於原始碼的規則建立了隱式標準。例如,Python 程式碼的縮排比 Perl(比如)要一致得多,因為空白(縮排)對於直譯器來說實際上是有意義的。Python 不使用 Perl 用來分隔函式的大括號語法。縮排的更改用作分隔符。[5][6] Tcl 使用類似於 Perl 或 C/C++ 的大括號語法來分隔函式,但不允許以下內容,這對 C 程式設計師來說似乎相當合理

 set i 0
 while {$i < 10} 
 {
    puts "$i squared = [expr $i*$i]"
    incr i
 }

原因是在 Tcl 中,大括號不僅用於像 C 或 Java 中那樣分隔函式。更一般地說,大括號用於將單詞組合成一個引數。[7][8] 在 Tcl 中,單詞 while 接受兩個引數,一個條件和一個操作。在上面的例子中,while 缺少第二個引數,即操作(因為 Tcl 還使用換行符來分隔命令的結束)。

常見約定

[編輯 | 編輯原始碼]

如上所述,常見的編碼約定可能涵蓋以下領域

  • 註釋約定
  • 縮排樣式約定
  • 命名約定
  • 程式設計實踐
  • 程式設計原則
  • 程式設計經驗法則
  • 程式設計風格約定

每行只應出現一條語句

[edit | edit source]

例如,在 Java 中,語句應該這樣寫

a++;
b = a;

而不是這樣

a++; b = a;

決策結構中的布林值

[edit | edit source]

一些程式設計師認為,當決策結果僅僅是計算一個布林值時,程式碼過於冗長且容易出錯。他們更喜歡在計算本身中進行決策,如下所示

return (hours < 24) && (minutes < 60) && (seconds < 60);

這完全是風格上的差異,因為最佳化編譯器可能會對兩種形式生成相同的目的碼。然而,在風格上,程式設計師對哪種形式更易讀和維護存在分歧。

支援較長形式的論據包括:它可以使程式設計師在決策的一個分支上設定單行斷點;可以在一個分支上新增更多程式碼行,而無需重構 return 行,這將增加引入錯誤的可能性;較長形式始終允許偵錯程式單步執行到變數仍在作用域內的行。

左端比較

[edit | edit source]

在使用一個符號(通常是單個等號 (=))進行賦值,另一個符號(通常是兩個等號 (==) 進行比較(例如 C/C++、Java、ActionScript 3、PHP、Perl 數值上下文以及過去 15 年中的大多數語言)的語言中,以及在控制結構中可能進行賦值的情況下,採用左端比較風格具有一定的優勢:在任何比較中將常量或表示式放在左側。 [9] [10]


以下是應用於 Perl 程式碼行的左端和右端比較風格。在這兩種情況下,這都會將變數 $a 中的值與 42 進行比較,如果匹配,則執行後續塊中的程式碼。

if ( $a == 42 ) { ... }  # A right-hand comparison checking if $a equals 42.
if ( 42 == $a ) { ... }  # Recast, using the left-hand comparison style.

當開發人員不小心輸入 = 而不是 == 時,就會出現差異

if ( $a = 42 ) { ... }  # Inadvertent assignment which is often hard to debug
if ( 42 = $a ) { ... }  # Compile time error indicates source of problem

第一行(右端)現在包含一個潛在的細微缺陷:它不再像以前那樣執行,而是將 $a 的值設定為 42,然後始終執行以下塊中的程式碼。由於這在語法上是合法的,因此程式設計師可能不會注意到錯誤,並且軟體可能會包含錯誤。

第二行(左端)包含一個語義錯誤,因為不能將數值賦值給它。這將導致在編譯程式碼時生成診斷訊息,因此程式設計師無法忽視錯誤。

一些語言內建了防止意外賦值的保護措施。例如,Java 和 C# 不會為了這個原因而支援自動轉換為布林值。

還可以透過使用可以檢測此問題的靜態程式碼分析工具來減輕風險。

迴圈和控制結構

[edit | edit source]

使用邏輯控制結構進行迴圈也有助於提高程式設計風格。它有助於閱讀程式碼的人更好地理解程式的執行順序(在指令式程式設計語言中)。例如,在虛擬碼中

 i = 0
 '' ''
 '''while''' i < 5
   '''print''' i * 2
   i = i + 1
 '''end while'''
 '' ''
 '''print''' "Ended loop"

上面的程式碼片段遵守了命名和縮排風格指南,但以下對“for”結構的使用可能被認為更易讀

 '''for''' i = 0, i < 5, i=i+1
   '''print''' i * 2
 '' ''
 '''print''' "Ended loop"

在許多語言中,經常使用的“for each element in a range”模式可以縮短為

 '''for''' i = 0 '''to''' 5
   '''print''' i * 2
 '' ''
 '''print''' "Ended loop"

在允許使用花括號的程式語言中,風格文件通常要求即使是可選的,花括號也應與所有控制流結構一起使用。

 '''for''' (i = 0 '''to''' 5) {
   '''print''' i * 2;
 }
 '' ''
 '''print''' "Ended loop";

這可以防止程式流錯誤,這些錯誤可能很耗時才能找到,例如在結構末尾引入終止分號(常見錯誤)的情況

 for (i = 0; i < 5; ++i);
    printf("%d\n", i*2);    /* The incorrect indentation hides the fact 
                               that this line is not part of the loop body. */
 
 printf("Ended loop");

...或者在第一行之前新增另一行

 for (i = 0; i < 5; ++i)
    fprintf(logfile, "loop reached %d\n", i);
    printf("%d\n", i*2);    /* The incorrect indentation hides the fact 
                               that this line is not part of the loop body. */
 
 printf("Ended loop");

列表

[edit | edit source]

當列表中的專案放在單獨的行上時,有時將專案分隔符新增到最後一個專案之後以及每個專案之間被認為是一種好的做法,至少在那些語法支援這樣做(例如 C、Java)的語言中是這樣。

const char *array[] = {
    "item1",
    "item2",
    "item3",  /* still has the comma after it */
};

這可以防止在重新排序列表專案或在末尾新增更多專案時出現語法錯誤或細微的字串連線錯誤,而程式設計師沒有注意到列表中先前最後一行上的“缺失”分隔符。但是,此方法可能會在某些語言中導致語法錯誤(或誤導性的語義)。即使對於確實支援尾隨逗號的語言,這些語言中並非所有類似列表的語法結構都可能支援它。

參考文獻

[edit | edit source]
  1. "Java 程式語言的程式碼規範 : 為什麼需要程式碼規範". Sun Microsystems, Inc. 1999-04-20.
  2. Jeffries, Ron (2001-11-08). "什麼是極限程式設計? : 設計改進". XP 雜誌.
  3. Hoff, Todd (2007-01-09). "C++ 編碼規範 : 命名類檔案".
  4. FIFE 編碼標準
  5. van Rossum, Guido (2006-09-19). "Python 教程 : 程式設計入門". Python 軟體基金會. {{cite web}}: 未知引數 |coauthors= 被忽略 (|author= 建議) (幫助)
  6. Raymond, Eric (2000-05-01). "為什麼選擇 Python?". Linux 雜誌.
  7. Tcl 開發者交流. "Tcl 語言語法概述". ActiveState.
  8. Staplin, George Peter (2006-07-16). "為什麼我不能在花括號組之前開始新行". 'the Tcler's Wiki'.
  9. Sklar, David (2003). PHP Cookbook. O'Reilly. {{cite book}}: Unknown parameter |coauthors= ignored (|author= suggested) (help), recipe 5.1 "Avoiding == Versus = Confusion", p118
  10. "C Programming FAQs: Frequently Asked Questions". Addison-Wesley, 1995. Nov. 2010. {{cite web}}: Check date values in: |date= (help)
[編輯 | 編輯原始碼]

語言編碼規範

[編輯 | 編輯原始碼]

專案編碼規範

[編輯 | 編輯原始碼]
華夏公益教科書