高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

Private Setter in Ruby

image photo by Brad Higham

之前在 Public, Protected and Private Method in Ruby 這篇文章提到,在 Ruby 裡使用 private 方法的時候,不能明確的指出 receiver,以下面這段範例來說:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Dog
  def hello
    self.gossip
  end

  private
  def gossip
    puts "don't tell anyone!"
  end
end

snoopy = Dog.new
snoopy.hello        # => NoMethodError
snoopy.gossip       # => NoMethodError

在上面這段程式碼的第 13 行,因為它明確的指出 receiver (snoopy),所以執行這行程式碼會出現會出現 NoMethodError 的錯誤;而在第 3 行,即使 receiver 是 self 也一樣是不行的。

這是在 Ruby 裡面 private 方法的設計。

不過,今天剛好有朋友拿了一段程式碼給我看,才發現原來上面這個規則原來也是有例外的,舉個例子來說:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Dog
  attr_accessor :name
  private :name, :name=

  def hello
    self.gossip
  end

  def greeting1
    self.name
  end

  def greeting2
    self.name = "Snoopy"
  end

  private
  def gossip
    puts "don't tell anyone!"
  end
end

snoopy = Dog.new
snoopy.hello         # => NoMethodError
snoopy.gossip        # => NoMethodError
snoopy.greeting1     # => NoMethodError
snoopy.greeting2     # => It Works!

在上面這段範例中用 attr_accessor 做了一個 name 的 getter 跟 setter,並且把 getter/setter 設定成 private。在第 10 行的地方呼叫了 private 的 getter,並且明確的指出 receiver 是 self,照規則來說會出現錯誤不意外,但第 14 行用類似的方法呼叫了 private 的 setter 卻沒有出錯!

的確 private 方法不能指出明確的 receiver,但 setter 算是這個規則的例外。因為你不在前面加 self 的話,像這樣:

1
2
3
  def greeting
    name = "Snoopy"
  end

這樣這個 name 不就變成區域變數的賦值了嗎?

想想看,如果在這裡呼叫 setter 不加 self 的話,那 Ruby 要怎麼分辨到底你是要呼叫 setter 還是區域變數?

話說回來,通常我們也不會沒事把 getter/setter 設定成 private,因為即然都做了 getter/setter 就是要給別人用的不是嗎? :)

Comments