天天看點

《Effective Ruby:改善Ruby程式的48條建議》一第4條:留神,常量是可變的

本節書摘來自華章出版社《effective ruby:改善ruby程式的48條建議》一書中的第1章,第1.4節,作者[美]彼得 j.瓊斯(peter j. jones),更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視

如果你學習ruby之前學過其他的程式設計語言,ruby中常量的行為很可能和你預料的不同。不過,在深挖之前讓我們回顧一下在ruby中什麼是常量。

當你初次學習ruby時,可能有人教你,說常量是由大寫字母和下劃線組成的辨別符。一些例子包括stdin、argv以及ruby_version。不過這不是故事的全部。事實上,由大寫字母開頭的任何辨別符都是常量。這表示string或array也都是常量。沒錯,就是這樣……ruby中那些類和子產品的名字事實上都是常量。記着這些,讓我們來深入了解在ruby中常量和其他看似變量的東西有什麼差別。

正如其名字建議的那樣,常量原打算在程式的生命周期中保持不變。是以,你可能假設ruby會阻止你改變常量的值的行為。好吧,你的假設是錯的。來看看這個:

《Effective Ruby:改善Ruby程式的48條建議》一第4條:留神,常量是可變的

如果調用方法unreachable時沒有加參數的話,就會意外地改變一個常量的值。在ruby中這樣做甚至都不會警告你。本質上講,ruby中的常量相比不變值來說更像是全局變量。假如你仔細想想,既然類和子產品名都是常量,那麼你可以在任何時候改變一個類(比如增加些方法),引用類的那些對象也會随之變化。這些對類和子產品來說都沒有問題,不過對那些我們真正希望是不變量的常量來說就不那麼美好了。還好,有一種解決這個問題的方法——freeze方法:

《Effective Ruby:改善Ruby程式的48條建議》一第4條:留神,常量是可變的

适當地做以上變化,假如再想改變常量networks的值時,purge_unreachable方法就會引發runtimeerror異常。根據一般的經驗,總是通過當機常量來阻止其被改變。然而不幸的是,當機networks數組還不夠,來看看這個:

《Effective Ruby:改善Ruby程式的48條建議》一第4條:留神,常量是可變的

如果第二個參數沒有指派,那麼host_addresses方法會修改數組networks的元素。即使數組networks自身被當機,但其元素仍然是可變的。你可能無法從數組中增删元素,但你一定可以對存在的元素加以修改。是以,如果一個常量引用了一個集合,比如數組或是散列,那麼請當機這個集合以及其中的元素:

《Effective Ruby:改善Ruby程式的48條建議》一第4條:留神,常量是可變的

(如果你恰巧使用了ruby 2.1或更高版本,你可以使用第47條中的小技巧直接當機字元串的字面量。這可以在防止元素被意外改變的同時節省一些記憶體。)

當機常量可以把一個隐晦的、難以發現的問題變成一個異常。這顯然是很好的。然而,這還不夠。即使當機了一個變量所指向的對象,你仍然會在給存在的常量賦予新值時引發問題。不信請看這個例子:

《Effective Ruby:改善Ruby程式的48條建議》一第4條:留神,常量是可變的

如你所見,在ruby中給存在的常量賦予新值是完全合法的。你也看到ruby産生了警告,說我們正在改變一個常量的值。不過僅此而已。還好,如果我們自己處理這些事情,可以讓ruby在我們意外地重定義常量時引發異常。不過這個解決方案有些笨拙,并且在某些時候顯得有些粗暴,但勝在簡單。要防止ruby為常量重新指派,可以當機常量所在的類或者子產品。你甚至可能會想結構化你的代碼,進而将所有常量都定義在它們各自的子產品中,進而隔絕freeze方法的影響。

《Effective Ruby:改善Ruby程式的48條建議》一第4條:留神,常量是可變的

定義常量時,你可以考慮在三個級别進行當機。前兩個比較簡單:當機常量引用的對象以及定義常量的子產品。這兩步防止了常量被改變和重新指派。第三種則有些複雜。我們看到,如果常量引用了一個字元串數組,我們就需要當機這個數組以及其所有元素。換句話說,需要深層當機該常量引用的這個對象。每個常量都會有所不同,我們隻需保證它被完全當機就可以了。

要點回顧

總是将常量當機,進而防止其被改變。

如果常量引用了一個集合對象比如數組或散列,那麼當機這個集合及其所有

元素。

要防止常量被重新指派,可以當機定義它的那個子產品。