變數的作用域最小化原則
起源
前幾天,趙明威在圖靈社群發表了“演算法導論學習之補漏:斐波那契數列”,該文中最後的 Java 程式中有一個 fibonacci 函式,如左欄所示 。我在評論中指出,這個函式應該如右欄這樣寫:
static BigInteger fibonacci(int num) { BigInteger x = BigInteger.ZERO; BigInteger y = BigInteger.ONE; BigInteger z; for(int i = 0; i < num; i++) { z = y; y = x.add(y); x = z; } return x; } |
static BigInteger fibonacci(int num) { BigInteger x = BigInteger.ZERO; BigInteger y = BigInteger.ONE; for(int i = 0; i < num; i++) { BigInteger z = y; y = x.add(y); x = z; } return x; } |
在左欄的程式中,變數 z 的作用域有 8 行,而右欄只有 4 行。
比較這兩個程式
可能有人會認為,修改後的程式每次迴圈都要重新定義變數 z,意味著每次迴圈都要重新在棧中分配一個區域性變數,導致效能沒有修改前的程式好。
我們使用 javac 分別編譯這兩個程式,然後使用 javap -c 分別把編譯後的 .class 檔案反彙編為 Java bytecode,如下所示:
從上圖中可以看出,除了因為宣告變數的順序不同,導致變數 z 在這兩個程式中分別是第 3 號和第 4 號變數,而變數 i 在這兩個程式中分別是第 4 號和第 3 號變數之外,這個兩個程式是相同的。也就是,它們的執行速度完全一樣。
在 fibonacci 函式中,各個量如下所示:
- 量 0: num
- 量 1: x
- 量 2: y
- 量 3: z 或 i
- 量 4: i 或 z
- 量 5: BigInteger.ZERO
- 量 6: BigInteger.ONE
- 量 7: BigInteger.add
實際上,區域性變數表在使用 javac 編譯這兩個程式時就決定了,在 fibonacci 函式被呼叫之前就分配好所有的區域性變數(包括函式的引數),在函式結束時隨退棧操作一起釋放。
變數的作用域最小化原則
根據《程式碼大全》第 10 章“使用變數的一般事項”第 4 節“作用域”:
- 作用域或者可見性指的是變數在程式內的可見和可引用的範圍。
- 減少變數作用域的方法之一就是儘量使變數區域性化。
- 當對變數的作用域猶豫不決的時候,你應該傾向於選擇該變數所能具有的最小的作用域:首先將變數侷限於某個特定的迴圈,然後是侷限於某個子程式,其次成為類的 private 變數、protected 變數,再其次對包可見(如果你的程式語言支援包),最後在不得已的情況下再把它作為全域性變數。
- 你應該把每個變數定義成只對需要看到它的、最小範圍的程式碼段可見。如果你能把變數的作用域限定到一個單獨的的迴圈或者子程式,那是再好不過的了。
- 一般而言,應使變數的作用域最小化,把變數引用點儘可能集中在一起,從而能夠對變數施加控制。將區域性變數的作用域最小化,可以增強程式碼的可讀性和可維護性,並降低出錯的可能性。
參考資料
相關文章
- 為什麼要將區域性變數的作用域最小化?變數
- 變數作用域變數
- JS變數作用域JS變數
- SCSS 變數作用域CSS變數
- python變數與變數作用域Python變數
- java中變數的作用域Java變數
- JavaScript 變數的作用域鏈JavaScript變數
- 『無為則無心』Python函式 — 30、Python變數的作用域Python函式變數
- C# 變數作用域C#變數
- Go語言中的變數作用域Go變數
- 現代 JavaScript 的變數作用域JavaScript變數
- Shell變數的作用域問題變數
- javascript中的作用域(全域性變數和區域性變數)JavaScript變數
- 變數物件與作用域鏈變數物件
- JavaScript之變數及作用域JavaScript變數
- JavaScript中變數和作用域JavaScript變數
- Go 中的動態作用域變數Go變數
- 函式(三)作用域之變數作用域、函式巢狀中區域性函式作用域、預設值引數作用域函式變數巢狀
- Python 函式和變數作用域Python函式變數
- Day08-常量、變數、作用域變數
- 變數、作用域與記憶體變數記憶體
- PHP 變數的四大作用域PHP變數
- CSS變數的作用域和預設值CSS變數
- golang變數作用域問題-避免使用全域性變數Golang變數
- 11-程式碼塊和變數的作用域變數
- ES6 變數作用域總結變數
- Java基礎06:變數、常量、作用域Java變數
- 語法1-變數、常量、作用域變數
- 原型模式故事鏈(5)--JS變數作用域、作用域鏈、閉包原型模式JS變數
- Go基礎知識-02 作用域 常量 變數 作用域(持續更新)Go變數
- 強大的CSS:var變數的區域性作用域(繼承)特性CSS變數繼承
- 迴圈輸出——閉包、變數作用域變數
- GO語言變數作用域-坑記錄Go變數
- JAVA基礎6-變數、常量、作用域Java變數
- js作用域(變數提升,預解析)例題JS變數
- java基礎06-變數、常量、作用域Java變數
- 動手動腦——同名變數的遮蔽原則變數
- 十二、變數作用域:區域性變數、全域性變數,函式版名片管理系統—新增函式文件變數函式
- JS 底蘊之 變數、作用域和垃圾回收JS變數