剖析 rudy 訪問控制

NO IMAGE

 前面,我們說Ruby沒有函式,只有方法.而且實際上有不止一種方法.這一節我們介紹訪問控制(access controls). 

想想當我們在”最高層”而不是在一個類的定義裡定義一個方法時會發生什麼.我們可以把這樣的方法設想為在像C那樣的傳統語言中的函式的類似物.

ruby> def square(n)
    |   n * n
    | end
   nil
ruby> square(5)
   25  

我們的新方法看起來不屬於任何類,但實際上Ruby將其分給Object類,也就是所有其它類的父類.因此,所有物件現在都可以使用這一方法.這本應是正確的,但有個小陷阱:它是所有類的私有(private)方法.我們將在下面討論這是什麼意思,但一個結果是它只能以函式的風格呼叫,像這樣:

ruby> class Foo
    |   def fourth_power_of(x)
    |     square(x) * square(x)
    |   end
    | end
  nil
ruby> Foo.new.fourth_power_of 10
  10000  

我們不允許向一個物件明確地運用這一方法:

ruby> “fish”.square(5)
ERR: (eval):1: private method `square’ called for “fish”:String  

這一聰明的做法在 ruby 使得提供可以像在傳統語言中那樣運用函式的同時保持了 ruby 的純OO性質(函式仍是物件方法,但接受者隱式的為self).

在OO程式設計裡,有一個不成文的習慣,我們在前面的章節裡有所暗示的,即有關規格(specification)和實現(implementation)的區別,或者說物件被要求完成什麼任務和實際上它是怎樣完成的.物件的內部工作應該對使用者保持隱蔽;他們應該只關心輸入什麼和輸出什麼,並相信物件知道它在內部是在做什麼.如此,某些外面世界看不見但卻在內部使用(並可以由程式設計師在任何需要的時候改進,而不用改變使用者看到的類的物件)的方法將很有用.在下面這個普通的例子裡,你可以把engine看作類的內部隱式方法.

ruby> class Test
    |   def times_two(a)
    |     print a,” times two is “,engine(a),”\n”
    |   end
    |   def engine(b)
    |     b*2
    |   end
    |   private:engine  # this hides engine from users
    | end
   Test
ruby> test = Test.new
   #<Test:0x4017181c>
ruby> test.engine(6)
ERR: (eval):1: private method `engine’ called for #<Test:0x4017181c>
ruby> test.times_two(6)
6 times two is 12.
   nil  

開始時,我們本希望test.engine(6)返回12,但隨後當我們扮演一個Test物件使用者時我們瞭解到engine是不可訪問的(inaccessible).只有Test的其它方法,像 times_two可以使用 engine.我們被要求保持程式的外部介面,也就是 times_two這些方法.管理此類的程式設計師可以自由的更改engine(這裡,可能把b*2改為b b,假設這樣可以提高效能)而不影響使用者與 Test 物件打交道.這個例子當然過於簡單;訪問控制的優點只有當我們開始寫更復雜和有趣的類時方能顯現出來.