iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 15
0
自我挑戰組

滿滿的紅寶石不拿嗎?-- 去吧!我把世界上的一切都放在那裡了! 系列 第 15

存取限制 <> 原來是海樓石!惡魔果實能力者的剋星 - 滿滿的紅寶石不拿嗎?

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20200923/20128363iznYQbH4HN.jpg

[Day15] 再厲害的能力者一碰到海樓石就會喪失戰鬥力!

今天要和大家介紹 Ruby 裡的存取控制(Access control)

先來看看維基百科怎麼說:

存取控制 是指允許或禁止某人使用某項資源的能力。

聽起來是不是超強!

不過在 Ruby 裡,存取控制其實就是方法的使用層級。當我們在類別裡定義方法時,還可以再更明確指出這個方法是只有誰才可以取用,共分為三種:

  • public
  • private
  • protected

Public

如果沒有特別寫是哪種存取方式,就會預設用 public 來存取我們定義的方法。

在撰寫存取控制的層級時,與方法定義前綴詞同一行即可:

class Cafe
  public # 通常直接省略不寫
  def coffee
    puts "來杯西西里拿坡里左岸八里漂浮拿鐵!"
  end
end

換句話說,平常不寫 public 也沒關係,除非你在定義了 private 方法後又要定義 public 方法才要特別加上去,不過通常我們是不會這樣做的,後面會提到原因。


Private

private 則是指所定義的方法不能有明確的訊息接收者(receiver),意思就是不能用.來呼叫方法

像是 puts 就是一種很常見的 private 方法,不過我們自己定義一個 private 方法 seasoning 試試看:

class Cafe
  def coffee 
    puts "來杯西西里拿坡里左岸八里漂浮拿鐵!" 
  end
  
  private
  def seansoning
    puts "還要有絕妙的香氣!"
  end
end

my_store = Cafe.new

my_store.coffee
my_store.seasoning

# 印出
來杯西西里拿坡里左岸八里漂浮拿鐵!
NoMethodError (undefined method `seasoning' for #<Cafe:0x00007f903c945538>)

我開的咖啡廳裡面沒有絕妙的香氣!


private 寫的方法要用在哪裡?

看來 private 的方法沒辦法像之前的方式直接呼叫,那它到底是用在哪裡呢?事實上,Ruby 裡用 private 寫的方法是拿來在類別內部呼叫的,像是這樣:

class Cafe
  def coffee
    puts "來杯西西里拿坡里左岸八里漂浮拿鐵!" 
    seasoning
  end
  
  private
  def seasoning
    puts "還要有絕妙的香氣!"
  end
end

my_store = Cafe.new

my_store.coffee

# 印出
來杯西西里拿坡里左岸八里漂浮拿鐵!
還要有絕妙的香氣!

private 方法在子類別也可以存取

在 Ruby 裡,private 寫的方法不只在類別內部可以存取,繼承自這個類別的子類別也一樣可以!

class Father
  private
  def heritage(gift)
    puts "#{gift} 是我留給兒子的財產!"
  end
end

class Son < Father
  def initialize(item)
    heritage(item)
    puts "謝謝老爸!"
  end
end

Son.new("PS5")

# 印出
PS5 是我留給兒子的財產!
謝謝老爸!

我也想要有個這樣的老爸(羨慕)


來看看 private 方法有哪些!

可以用 self.class.private_methods.sort 來找 :

self.class.private_methods.sort
 => [:Array, :Complex, :DelegateClass, :Float, :Hash, :Integer, :Rational, :String, :URI, :__callee__, :__dir__, :__method__, :`, :abort, :at_exit, :binding, :block_given?, :caller, :caller_locations, :catch, :eval, :exec, :exit, :exit!, :extended, :fail, :fork, :format, :gem, :gem_original_require, :gets, :global_variables, :included, :inherited, :initialize, :initialize_clone, :initialize_copy, :initialize_dup, :irb_binding, :iterator?, :lambda, :load, :local_variables, :loop, :method_added, :method_missing, :method_removed, :method_undefined, :open, :p, :prepended, :print, :printf, :private, :proc, :protected, :public, :putc, :puts, :raise, :rand, :readline, :readlines, :remove_const, :require, :require_relative, :respond_to_missing?, :select, :set_trace_func, :singleton_method_added, :singleton_method_removed, :singleton_method_undefined, :sleep, :spawn, :sprintf, :srand, :syscall, :system, :test, :throw, :trace_var, :trap, :untrace_var, :using, :warn] 

發現了嗎? privatepublic都是 private 方法!


其實也不是這麼 private

那... private 裡面的方法是不是就再也無法拿出來用呢?其實也未必。我們可以用 .send() 來取出 private 方法,只是這樣會破壞封裝的原則,非不得已建議不要這樣做。

class Man
  private
  def whisper
    puts "這是只有 private 才聽得到的悄悄話..."
  end
end

man = Man.new
man.send(:whisper)  

# 印出
這是只有 private 才聽得到的悄悄話...

果然成功拿出來了!


Protected

這是一個既不 public 又不 private 的東西。

protectedprivate 最大的不同,是 protected 可以允許在呼叫時前面有 receiver,這種承繼自 SmallTalk 的設計理念是 Ruby 的一大特點,不過可能之後才會詳細介紹這段。

但由於實際上現在幾乎沒有人在用 protected 寫 Ruby 的存取控制,就讓它慢慢地成為時代的眼淚吧!

class Smalltalk

  def say_hello_to_myself
    self.hello
  end

  protected
  def hello
    puts "murmur..."
  end
end

如果是用 protected,那麼在內部呼叫時要寫 self.hellohello 都可以,但改成寫 privateself.hello 就會出錯了。


存取控制的另一種寫法

也可以這樣寫:

class Gem
  def ruby
  end

  def emerald
  end

  def diamond
  end
  
  protected :emerald
  private :diamond
end

這麽一來,emerald 就變成了 protected 方法,而 diamond 成為了 private 方法


總結

publicprotectedprivate 的特點有三:

  1. 類別內部都可以呼叫 3 種類型的方法。
  2. 只有 public 方法能在外部被呼叫。
  3. 正常情況下 private 的方法不允許有 receiver, protected 則沒有這種限制。不過用 send() 可以取消 private 原本的限制。

在實際撰寫時,我們會把 private 的方法定義在類別的最下方,這樣就能知道 private 上方的通通都是 public 的方法,也能更輕鬆地閱讀程式碼。


關於 Ruby 方法的存取控制,今天就介紹到這邊啦!

參考來源:
高見龍 - Public, Protected and Private Method in Ruby


上一篇
類別方法 <> 雜魚們!為革命軍獻出你們的心臟吧! - 滿滿的紅寶石不拿嗎?
下一篇
符號 <> 看!又是一塊歷史正文!讀懂這些,通往 ONEPIECE 的路就不遠了!- 滿滿的紅寶石不拿嗎?
系列文
滿滿的紅寶石不拿嗎?-- 去吧!我把世界上的一切都放在那裡了! 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言