作为一个 Ruby 方法,与返回对象的指令块相关联 message handler
一般都是在后台运行。所以,private
和 protected
策略也就和消息 ( message ) 概念息息相关了。
这么拗口,读到这里,你可能和作者我一个想法:private
和 protected
一般不都是用来修饰类成员的吗,什么是 private
和 procted
策略呢 ?
哈哈,别着急,为了方便大家理解 private 和 protected 策略,我们先快速回顾一下 Ruby 消息概念
消息 ( Message ) 、接收器 ( Receiver ) 和 消息处理器 ( Message Handler )
消息 由名称 ( 通常是符号 ( symbol
) ) 和可选的有效内容 ( payload ) ( 也就是参数列表 ) 组成
一个消息需要一个接收器 ( receiver
,通常是一个对象 ) 通过 消息处理器 ( 通常是一个方法 ) 来响应和处理
而消息的发送方始终是调用对象上下文。如果从类上下文外部调用消息,那么就是 main
对象
我们可以使用 Kernel#send
方法向接收者明确发送消息
receiver name payload | | | __________ ______ ______ "a-string".send(:split, '-', 3)
上面的代码中,一个消息由以下几个部分组成
- 接收者
"a-string"
- 消息名称
:split
- 消息内容 ( payload ) :
'-',3
- 发送者:
main
对象
这段代码,我们也可以做如下解释
"a-string"
通过消息处理器 String#split
响应了一个名为 :split
的消息。
拗口的要死,其实你应该更熟悉另一个语法
```ruby receiver name payload | | |
"a-string".split('-', 3) ```
上看这段代码,我们可以这样理解: 一个带有 '-',3
消息内容的名为 split
的消息立即被发送给接收者 a-string
解释来解释去,不知道你是否已经理解了 Ruby 中消息的概念。如果理解了,那么我们就继续讲解 private
和 protected
方法的概念
私有 ( private ) 方法
Ruby 中,一个私有的方法或私有的消息处理器只能够响应具有隐式接收器 ( self
) 的消息。它无法响应从私有消息处理程序上下文(对象)外部调用的消息
也就是说,私有消息或私有方法只能处理对象内部的消息,也只能发给对象内部的接收器。
class Receiver def public_message private_message end def self_public_message self.private_message end private def private_message puts "This is a private message" end end irb> Receiver.new.public_message This is a private message => nil irb> Receiver.new.self_public_message NoMethodError: private method `private_message' called for #<Receiver:0x007b> irb> Receiver.new.private_message NoMethodError: private method `private_message' called for #<Receiver:0x007b>
上面的代码中,在 Receiver#public_message
方法内部,Receiver
实例隐式的发送了私有的消息 private_message
给接收器 self
。因为我们处于 Receiver
上下文中,而且消息接收器是隐式的 ( self
) ,因此 Receiver#private_message
是可以响应此消息的。
则 Receiver#self_public_messsage
则明确的调用了接收器 self
的私有方法。因为私有的消息处理器无法响应此接收器,所以会抛出 NoMethodError
异常。
对 Receiver.new.private_message
的显式调用将引发 NoMethodError
,因为该消息是从private_message
上下文(应该是 Receiver 的实例)外部发送的。
受保护 ( protected ) 方法
在 Ruby 中,受保护的方法( 或受保护的消息处理程序 )只能响应具有同族的隐式/显式接收器( 对象 )的消息。它也无法响应从受保护的消息处理程序上下文外部发送的消息
class Receiver def public_message protected_message end def self_public_message self.protected_message end protected def protected_message puts "This is a protected message" end end class Mailbox < Receiver def mb_public_message ::Mailbox.new.protected_message end end irb> Receiver.new.public_message This is a protected message => nil irb> Receiver.new.self_public_message This is a protected message => nil irb> Mailbox.new.mb_public_message This is a protected message => nil irb> Receiver.new.protected_message NoMethodError: protected method `protected_message' called for #<Receiver:0x007fbed691bdf0>
在 Receiver#public_message
方法中,protected
和 private
方法共享相同的策略
Receiver#self_public_message
显式调用接收者 self 的受保护方法,因此受保护的消息处理程序 Receiver#protected_message
可以响应消息,因为它包含
- 同族 ( 在一个继承体系内 ) 的接收器
- 消息由 Receiver 对象发送
Mailbox.new.mb_public_method
方法也可以正常工作,原因与上面列举的相同
但,Receiver.new.protected_message
则会引发 NoMethodError
异常,因为消息是从 Receiver 对象外部发送的
## Kernel#send 方法
Kernel#send
方法的某个特性在一定的条件下 ( 比如测试 ) 是非常有用的。这个特性就是:
当使用此方法发送消息时,将绕过私有策略和受保护策略
ruby
class Receiver
def public_message
protected_message
end
def self_public_message
self.protected_message
end
protected
def protected_message
puts "This is a protected message"
end
private
def private_message
puts "this is a private message"
end
end
irb> Receiver.new.send(:private_message)
this is a private message
=> nil
irb> Receiver.new.send(:protected_message)
This is a protected message
=> nil