Ruby 存取控制

圖片作者 Jozef Krajčovič  來源 http://jozefkrajcovic.sk/
圖片作者 Jozef Krajčovič
來源 http://jozefkrajcovic.sk/

Ruby 是一個完全物件導向程式語言,不管是字串、整數等,都是類別的物件。

在物件導向內,類別 (class)主要有方法 (method) 等組成,藉由呼叫方法來改變物件的狀態,Ruby 對方法的存取控制分為三個不同的層級,名稱與其他物件導向程式語言一樣,有 publicprotectedprivate

Public、Protected、Private 存取控制

在 Ruby 這三個存取控制層級與其他語言有不同之處,從其他語言跳到 Ruby 可能會忽略掉。

  • public 可以被任何人直接存取,例如:類別的物件可以呼叫 public 方法,預設所有的方法為 public,除了 #initialize 方法為 private
  • protected 層級比 public 小,只有相同類別或子類別內,才能存取 protected 方法
  • private 不能有明確的 receiver 呼叫 private 方法,表示 private 方法只能在物件內被呼叫

還是以下藉由程式來說明會更清楚!

存取控制

接著介紹撰寫方法的存取控制:

第一種寫法:

class Person
  def initialize (name, age)
    @name = name
    @age = age
  end

  def introduce 
    talk "Hello! My name is #{@name}"
    talk my_age
  end

  # starting of protected access control level
  protected
  def my_age
    "I'm #{@age} years old."
  end

  # before private, ending of protected access control level
  private
  def talk(sentance)
    puts sentance
  end
end

第一種寫法是將存取控制層級寫在方法前,此種寫法是存取控制層級後面的方法,都是遵照前面所寫的,直到遇到另一個存取控制層級。

如上面的程式碼,寫在 protected 之後的方法都是 protected 的存取層級,直到下一個存取層級,也就是 private,所以,在 protectedprivate 前的方法都是 protected

Person 類別內所有方法的存取層級分別為:

  • #initializeprivate
  • #introducepublic
  • #my_ageprotected
  • #talkprivate

PS. 在前面有提到,#initialize 預設為 private

第二種寫法:

class Person
  def initialize (name, age)
    @name = name
    @age = age
  end

  def introduce 
    talk "Hello! My name is #{@name}"
    talk my_age
  end

  def talk(sentance)
    puts sentance
  end

  def my_age
    "I'm #{@age} years old."
  end

  protected :my_age
  private :talk
end

第二個寫法是在方法之後,才定義方法的存取層級,分別在 protectedprivate 方法後面利用 symbol 來定義每個方法的存取控制層級。

接著我們來操作 Person 類別:

require_relative "person"

person = Person.new "AMing", 24
person.introduce

# Person#my_age is protected method
#person.my_age

# Person#talk is private method
#person.talk

如果外部程式,要呼叫 Person#my_agePerson#talk 會發生錯誤,因為兩個方法分別為 protectedprivate

繼承

建立一個 Male 類別,Male 繼承了 Person 類別:

require_relative "person"

class Male < Person
  def introduce
    super
    # In parent class, Person#talk is private method
    talk my_sex
  end

  private
  def my_sex
    "My sex is male."
  end
end

會發現第 7 行程式,在 Male#introduce 內,呼叫了 Person#talk,但是此方法為 private,為什麼 Ruby 可以這樣使用呢?

在一開始介紹 Ruby 的 private 存取控制層級就有提到,private不能被明確的 receiver 呼叫。在第 7 行內,#talk 並不沒有 receiver,所以在這裡是合法的,但是如果將第七行改成 self.talk my_sex 就會出現問題,因為 #talk 已經被明確的 receiver 也就是 self 呼叫了!

操作 Male 類別:

require_relative "male"

male = Male.new "Johnson", 26
male.introduce

不過,還是有可以在外面程式呼叫 protected 及 private 方法,去參加開開心心學 Ruby 的時候,大大有提到可以利用 Object#send 來呼叫 protectedprivate 方法。

require_relative "male"

male = Male.new "Johnson", 26
male.introduce

# use Object#send
puts male.send :my_age
male.send :talk, "private method is called"

不過這樣做,並沒有違反 Ruby 定義的 protectedprivate,以 private 來說,因為並不是由 receiver 去呼叫的,而是透過 #send 方法。

更多詳細的介紹可以參考:http://blog.eddie.com.tw/2011/07/26/public-protected-and-private-method-in-ruby/

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料