Ruby中Public, Protected and Private方法深度理解
如果你曾經在別的程式語言寫過OOP,你也許對類別的方法存取限制不會太陌生。類別的方法的存取限制常見的有三種:public、protected以及private。
這三種存取限制,比較常聽到的解釋大概會是像這樣:
“public就是所有的人都可以直接存取,private是只有在類別內部才可以存取;而protected差不多是在這兩者之間,比private寬鬆一些,但又沒有public那麼自在,protected在同一個類別內或是同一個package,或是繼承它的子類別可以自由取用,但如果不是的話則不可存取。”
Ruby也有類似的方法存取限制,為什麼特別說「類似」,前面又為什麼需要特別提「大家常聽到的解釋」,因為Ruby在這部份的實作是不太一樣的,待會後面會再詳細說明。
怎麼做?
先來看看怎麼寫,Ruby的方法存取限制有兩種寫法,一種是寫在方法定義之前:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
在Ruby的類別裡,方法只要沒有特別限制預設就是publilc
的,除了一個例外,就是initialize
方法,它永遠是private的,只會被new
方法呼叫。
把存取控制放在前面的這種寫法,只要在它設定之後的方法定義都會受影響,除非又遇到另一個存取控制的設定。在上面的這段程式碼,method_a跟method_b沒有特別限制,所以是public方法(如果你想要特別加上public也沒問題,只是通常不會這麼做),method_c是protected方法,而method_secret則是屬於private方法。
另一種的方法存取限制是寫在方法定義之後:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
哪種比較好?
這兩種寫法哪種方法比較好? 都好,隨個人喜好。我個人喜好第一種,因為我習慣會先把public的方法放在類別的上半部,而把private方法放在類別的最底下,所以使用第一種寫法對我來說寫起來比較順手。
其實public、protected以及private這三個在Ruby裡並不是關鍵字,它也只是Ruby裡的方法而已。
哪裡不一樣?
前面為什麼會特別提到Ruby的方法存取限制跟其它的程式語言「類似」呢? 雖然Ruby裡的確也有public、protected以及private,但事實上是不太一樣的,特別是private方法。我們先來看一小段的程式碼:
1 2 3 4 5 |
|
father是Father類別生出來的實體,而實體的public方法如所預期的印出結果,protected跟private方法呼叫的時候產生NoMethodError的例外,看起來很正常,那到底是哪邊不太一樣?
我們再來做個叫做Son的子類別,繼承自Father類別:
1 2 3 4 5 6 7 8 9 10 11 |
|
我給Son類別加了兩個方法,分別會呼叫Father類別的protected跟private方法,再來看範例:
1 2 3 4 5 6 7 |
|
在子類別呼叫父類別的protected方法,這不是新鮮事,但你注意到了嗎? 在子類別裡可以直呼叫父類別的private方法耶!
先來看這行:
1 |
|
一般我們會把這行翻譯成:
“有一個物件叫做son,然後呼叫了son物件的method_a方法”
不過如果你曾經認識過Smalltalk或是Objective-C的話,你會發現他們會把這行翻譯成:
“有一個接收者(receiver)叫做son,然後對著這個recevier送了一個叫做method_a的訊息(message)”
為什麼特別提這個? 因為在Ruby裡的private方法,只要沒有明確的指出recevier的話就都可以呼叫。所以在上面例子裡的Son類別,即使是呼叫父類別的private方法,只要不要有recevier,它就不會有錯誤產生。
也就是因為這樣,在Ruby的private方法其實不只類別自己內部可以存取,它的子類別也可以。再來看一下這段程式碼:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
在A類別的a方法呼叫類別內部的b方法,看起來很合理吧,但實際執行就會出錯NoMethodError的例外,說你存取到了private方法了!! 為什麼? 因為你在呼叫方法b的時候加上了self
,前面有提到”在呼叫Ruby的private方法時,不能明確的指定recevier”,在這裡卻明確的指出了self
,所以出現錯誤訊息了。沒錯,連self也不行。
我們很常用的puts
方法,它就是Object這個類別的private方法之一(更正確的說,是Kernel模組mixin到Object類別裡的方法)。我們平常會這樣用:
1 |
|
但如果你這樣做:
1 |
|
就會跳出NoMethodError
。
Protected方法?
那protected方法呢? 它的規定就沒那麼嚴格了,你要指定或不指定recevier都可以;至於public方法,就跟其它語言的定義差不多,隨便你用啦。
真的這麼private?
不過,其實Ruby的private方法也不是真的那麼private,轉個彎,一樣可以被外部呼叫:
1 2 3 |
|
前面提到的puts
其實也可以改寫成:
1 |
|
這..這樣會不會有點扯? 如果連private都能被直接存取,那當初何必還要這樣設計呢? 還是直接乾脆全部都public就好了?
我想這其實是Ruby當初設計的哲學之一,Ruby把很大部份的權利都下放給程式設計師,讓開發者有最大的彈性空間可以運用(或惡搞),也就是這樣,在Ruby做Metaprogramming是相對的比在別的程式語言容易的。不只在這裡,你應該還可以在很多地方看到這個Ruby的專屬特性。
僅供參考
如果說這些存取限制只是”參考用”,那到底什麼時候會用到?
雖然說它只是”參考用”,我個人還是會把它當做是程式碼的寫作準則。雖然你可以透過send方法來存取private方法,但不代表你就應該這樣做。而且它也不是真的那麼沒有用,像是在寫Rails的時候,Controller裡的Action預設都是public的,如果你的routes.rb如果把路徑的對應全部打開,那所有的Action都有可能透過網址而被存取到,那也許不會是你想要的結果。
原文链接: https://kaochenlong.com/2011/07/26/public-protected-and-private-method-in-ruby/