子程序(routines)是為實現一個特定功能而編寫的一個可被調用的方法(method)、函數(function)或過程(procedure)。如Java中的方法,C++里的函數。現代編程語言如Java、C++、VB、JavaScript、Ruby等都同時支持函數和過程。
一般認為函數指具有返回值的子程序,過程指沒有返回值的子程序。C++中把所有子程序成為函數,其實那些返回值為void的函數在語義上也是過程。函數與過程的區別更多是語義上的區別,而不是語法的區別。
語言純化論者認為一個函數應該只有一個返回值,這和數學函數一樣。即函數只接受輸入(參數),通過參數運算返回結果。 除此之外的效果被稱為函數的副作用,比如修改全局變量。
以JS示例
function sum1(x, y) { return x+y } function sum2(x, y) { alert(x+y) }
sum1是一個函數,它有輸入並返回結果;sum2則是過程,接受輸入,處理輸入(打印輸出結果),但沒有返回結果。
好的子程序需要遵循以下原則
- 高內聚
- 好的命名
- 長度適中
- 合理的參數
一、高內聚性
內聚性是計算機科學里很重要的一個概念,由Larry Konstantin在1974年的一篇論文提出。它由分為以下
1. 功能內聚(Functional cohesion,最高)
最好最強的一種內聚性,即一個子程序僅執行一個操作,有的書也稱“只做一件事,做好一件事”。這種子程序執行的操作與其名稱多數是相符的,如sum執行相加,deletePage刪除頁面。
2. 順序上的內聚(Sequential cohesion)
指子程序內需按特定順序執行操作,這些步驟需要共享數據,且在全部執行后才完成子程序的完整功能。比如需要先計算A,再使用A計算B,接着取B計算C。
3. 通信上的內聚(Communicational cohesion)
是指子程序不同操作使用了相同數據,但不存在任何聯系。
4. 臨時的內聚性(Temporal cohesion)
是指含有一些因為需要同時執行才放到一起的操作的子程序。
5. 邏輯上的內聚性(Logical cohesion)
是指若干操作被放入同一個程序中,通過傳入的控制標志選擇執行其中的一項操作。
6. 偶熱的內聚性(Coincidental cohesion 最低)
指子程序中各個操作直接沒有可以看到的內聯,也稱為“無內聚性”或“混亂的內聚性”。
二、好的命名
好的命名能清晰的描述子程序所做的一切。以下是一些命名注意事項
1. 描述子程序所完成的功能
2. 避免使用無意義的、模擬或表達不清的詞
3. 不要僅通過數字來區分不同的子程序名
4. 根據需要確定子程序名字的長度
5. 對返回值要有所描述
6. 一般是動詞+名詞形式
7. 使用對仗詞,如add/remove, begin/end, first/last, get/put, up/down/, show/hide, open/close。
三、長度適中
“子程序/函數的第一要素就是短小,第二條規則還是短小”,鮑勃大叔如此說。理論上認為子程序的長度最大長度通常是一屏代碼,大約50-150行。
一項對子程序的研究發現,平均100-150行代碼的子程序需要修改的幾率最低(Lind and Vairavan 1989)。
IBM一項研究發現,最容易出錯的是那些超過500行代碼的子程序。超過500行后,子程序的出錯率和代碼行數成正比。在面向對象編程中,一大部分子程序都是訪問器子程序(getter),它們都非常短小。任何時候復雜算法總會導致較長的子程序,這種情況下允許長度增加到100到200行。
四、合理的參數
子程序之間的接口是程序中最易出錯的地方,Basili和Perrricone所做的一項研究發現程序中39%的錯誤都是屬於內部接口錯誤。也就是子程序間互相通信時所發生的錯誤。應該按以下原則處理
1. 按照輸入-修改-輸出的順序排列參數。不要隨機地按字母順序排列參數,而應先列出輸入參數,然后是即作為輸入又作為輸出的參數,最后是輸出的參數。比如Ada就要專門的關鍵字in,out。
procedure InvertMatrix { originalMatrix: in Matrix; resultMatrix: out Matrix; }
2. 如果幾個子程序都用了類似的一些參數,應該讓這些參數的排列順序一致。
dom = { setWidth: function(elem, value) { // ... }, setHeight: function(elem, value) { // ... } }
3. 使用所有的參數,既然定義了該參數就應該使用它,如果不用它就應該刪掉它。
4. 把狀態或出錯的變量放在后面,狀態和那些用於指示發生錯誤的變量應放在參數表最后。它們只是程序的附屬功能且只用於輸出的參數。
5. 不要把子程序的參數用於工作變量
function process(inputVal) { inputVal = inputVal - 10; return inputVal }
這段JS代碼中,省略了一個變量聲明,inputVal很容易讓人誤解,即是輸入又是輸出,改為如下
function process(inputVal) { var outputVal = inputVal - 10; return outputVal }
6. 子程序的參數個數限制在7個以內。鮑勃大叔說的更極端 “最理想的參數數量是零,其次是單參數函數,再次是雙參數函數,應盡量避免三參數函數”。心理學研究發現,人類很難記住超過7個單位的信息。這一發現已應用在各個領域。
相關: