如何處理代碼中的魔術數字(Magic Number)

我真的很不喜歡“魔術數字(Magic Number)”這個說法。我看到很多人都搞錯了。我見過不少次 code review,有人一看到代碼中的任何數字,就會評論說,"這是一個魔術數字,一定要把它放到文件前面並定義一個名字。"

(我也比較懷疑把所有的變量都放在文件最前面的必要性,這個話題下次再聊)。

我覺得可以在代碼中使用各種數字,只是你需要注意操作方式。

什麼是魔術數字?

通過谷歌搜索,你可以得到一堆蹩腳的定義,但底線是,一個魔術數字,是你的代碼中很難推理的數字。

<code>fun generate {/<code><code> for (i in 0 until 52) {/<code><code> deck[i] = uniqueCard/<code><code> }/<code><code>}/<code>

52 是哪裡來的?

原來這段代碼是要生成一副撲克牌,因為 52 是一副撲克牌的數量,因此我們可以給這個數字起個名字。

<code>const val numberOfCardsInADeck = 52/<code>
<code>fun generate {/<code><code> for (i in 0 until numberOfCardsInADeck) {/<code><code> deck[i] = uniqueCard/<code><code> }/<code><code>}/<code>

這樣就得到可讀性更強、可維護性更強的代碼。很好,你已經掌握了編寫乾淨代碼的方法。

但是,這只是冰山一角。這個例子的問題是,開發者很容易就能從代碼的其他部分中找出 52 是什麼,這是一個相當簡單的魔法數字。

魔法數字真正讓你頭痛的地方是,不理解它從哪裡來的,拿下面調整搜索算法的代碼來說吧。

<code>fun search(query: String) {/<code><code> find(query, 2.4f, 10.234f, 999, Int.MAX_VALUE, false)/<code><code>}/<code>

這一堆數字到底是什麼意思?要理解這些數字是幹什麼用的並不容易。

魔法數字的問題是什麼?

假設你的應用規模越來越大,需要搜索的東西越來越多,突然間你的搜索結果並沒有得到你想要的結果。

我們有這樣的問題:當我搜索麥片時,結果中沒有出現麥片,儘管我知道它在裡面。

所以在這個算法被調整了四年之後,你需要改變這些值來修復這個 bug,該如何入手?

這就是魔法數字的問題。如果把這些數字用長長的描述性的名字組合在一起,再加上代碼文檔,詳細說明改變它們對搜索結果的影響,維護起來會更簡單。

在解釋算法方面也要加分。

讓我們修正一下這個問題吧。

<code>const val searchWeight = 2.4f // How specific your query must be. Increase this number to get more fuzzy results/<code><code>const val searchSpread = 10.234f // How spread the result are. Selects more words in a row in the database/<code><code>const val searchPageSize = 999 // The number of results we want per search page/<code><code>const val searchMaxResults = Int.MAX_VALUE // We want every possible result from the search/<code><code>const val shouldSearchIndex = false // We don't want to search indicies/<code>
<code>fun search(query: String) {/<code><code> find(query, searchWeight, searchSpread, searchPageSize, searchMaxResults, shouldSearchIndex)/<code><code>}/<code>
<code>// Calls our weighted search algorithim. Read the docs about this alogirthim at foo.bar.com/<code><code>fun find(query: String, weight: Float, spread: Float, pageSize: Int, maxResults: Int, index: Boolean) {}/<code>

你會不會覺得維護這個代碼更舒服?如果有人能用這個文檔來解決這個 bug,那就更有底氣了。

什麼不是魔法數字?

現實中,難於推理的數字並不像容易推理的數字那樣經常出現,以這些硬編碼的數字為例

<code>view.height = 42/<code>

這不是一個魔法數字。我重複一遍:這不是一個魔法數字。

我知道,這是在對一些 Java 純潔主義和有潔癖的人發神經了。

但這個數字並不難理解。它的作用完全是自成一體的:這個視圖的高度是42,僅此而已。如果再給它另外起個名字,又能增加什麼價值?

<code>const val viewHeight = 42/<code>
<code>fun buildView {/<code><code> view.height = viewHeight/<code><code>}/<code>

這不過是臃腫的代碼罷了。這似乎是個小例子,但這種無謂地給數字命名的想法很快就會使 UI 代碼的大小膨脹,只會增加無意義代碼的行數。

那我的代碼中到底能不能用數字呢?

當然可以。世界上有很多好的代碼都是用數字來寫的。你只需要牢記幾件事就可以了。

  • 確保你的數字是容易理解 — 比如小學生都可以理解這個數字的作用。

  • 如果你要改變一個數字,調整一些東西,或者在紙上做一些計算來得到一個硬編碼的數字,要解釋清楚。在代碼中,就在數字旁邊。或者至少在提交中說明。對硬編碼數字的更改要有解釋。

  • 獎勵:確保你的硬編碼數字是 DRY(非重複的)。

這不是火箭科學,但使用你的數字有很多微妙的地方。

你應該可以搞定這些,謝謝你的閱讀!

感謝 Zack Shapiro.

原文地址:

https://medium.com/better-programming/magic-numbers-are-not-that-magic-132297d435f5

高可用架構

改變互聯網的構建方式


分享到:


相關文章: