有没有办法从 Ruby 中的实例调用私有类方法?
Posted
技术标签:
【中文标题】有没有办法从 Ruby 中的实例调用私有类方法?【英文标题】:Is there a way to call a private Class method from an instance in Ruby? 【发布时间】:2010-09-06 10:44:05 【问题描述】:当然,除了self.class.send :method, args...
。我想在类和实例级别都提供一个相当复杂的方法,而无需复制代码。
更新:
@Jonathan Branam:这是我的假设,但我想确保没有其他人找到解决办法。 Ruby 中的可见性与 Java 中的可见性非常不同。你也很正确,private
不适用于类方法,尽管这将声明一个私有类方法:
class Foo
class <<self
private
def bar
puts 'bar'
end
end
end
Foo.bar
# => NoMethodError: private method 'bar' called for Foo:Class
【问题讨论】:
【参考方案1】:这里有一个代码 sn-p 来解决这个问题。在类定义中使用“private”不适用于类方法。您需要使用“private_class_method”,如下例所示。
class Foo
def self.private_bar
# Complex logic goes here
puts "hi"
end
private_class_method :private_bar
class <<self
private
def another_private_bar
puts "bar"
end
end
public
def instance_bar
self.class.private_bar
end
def instance_bar2
self.class.another_private_bar
end
end
f=Foo.new
f=instance_bar # NoMethodError: private method `private_bar' called for Foo:Class
f=instance_bar2 # NoMethodError: private method `another_private_bar' called for Foo:Class
我没有办法解决这个问题。文档说您不能指定私有方法的接收。此外,您只能从同一实例访问私有方法。 Foo 类与 Foo 的给定实例是不同的对象。
不要把我的回答当成最终的。我当然不是专家,但我想提供一个代码 sn-p,以便其他尝试回答的人将拥有适当的私有类方法。
【讨论】:
【参考方案2】:让我为这个或多或少奇怪的解决方案和非解决方案列表做出贡献:
puts RUBY_VERSION # => 2.1.2
class C
class << self
private def foo
'Je suis foo'
end
end
private define_method :foo, &method(:foo)
def bar
foo
end
end
puts C.new.bar # => Je suis foo
puts C.new.foo # => NoMethodError
【讨论】:
按照原来的post请求,调用类方法。这不好,因为它将方法复制到类的实例方法集中。例如,如果您对类方法 C::foo 进行猴子修补,您会突然在类级别 ::foo 和实例级别 #foo 之间出现不匹配的行为。【参考方案3】:现在您不再需要辅助方法了。您可以简单地将它们与您的方法定义内联。这对 Java 人员来说应该很熟悉:
class MyClass
private_class_method def self.my_private_method
puts "private class method"
end
private def my_private_method
puts "private instance method"
end
end
不,您不能从实例方法调用私有类方法。但是,您可以改为在 private 嵌套类中将 private 类方法实现为 public 类方法,使用 private_constant
辅助方法.详情请参阅this blogpost。
【讨论】:
【参考方案4】:如果你的方法仅仅是一个utility function(也就是说,它不依赖于任何实例变量),你可以把这个方法放到一个模块和include
和extend
这个类中,这样它就可以作为私有类方法和私有实例方法。
【讨论】:
【参考方案5】:这是使用“真正的”私有类方法的方式。
class Foo
def self.private_bar
# Complex logic goes here
puts "hi"
end
private_class_method :private_bar
class <<self
private
def another_private_bar
puts "bar"
end
end
public
def instance_bar
self.class.private_bar
end
def instance_bar2
self.class.another_private_bar
end
def calling_private_method
Foo.send :another_private_bar
self.class.send :private_bar
end
end
f=Foo.new
f.send :calling_private_method
# "bar"
# "hi"
Foo.send :another_private_bar
# "bar"
干杯
【讨论】:
第一个示例出错:1.9.3p327 :078 > f=Foo.new => #<Foo:0x0000000293baa0> 1.9.3p327 :079 > f.class.send :calling_private_method NoMethodError: undefined method `calling_private_method' for Foo:Class from (irb):79 from ~/.rvm/rubies/ruby-1.9.3-p327/bin/irb:16:in `<main>'
是的,这不起作用(或者可能不再起作用)。
@metakungfu 我(仍然)有问题的部分是从任何实例方法调用私有类方法。在实例方法中,self.class.some_private_method
将引发错误。困扰我的是,除非我们使用反射,否则私有类方法只能被其他类方法使用。【参考方案6】:
这可能是最“原生香草 Ruby”的方式:
class Foo
module PrivateStatic # like Java
private def foo
'foo'
end
end
extend PrivateStatic
include PrivateStatic
def self.static_public_call
"static public #foo"
end
def public_call
"instance public #foo"
end
end
Foo.static_public_call # 'static public foo'
Foo.new.public_call # 'instance public foo'
Foo.foo # NoMethodError: private method `foo' called for Foo:Class
Foo.new.foo # NoMethodError: private method `foo' called for #<Foo:0x00007fa154d13f10>
通过一些 Ruby 元编程,您甚至可以使它看起来像:
class Foo
def self.foo
'foo'
end
extend PrivateStatic
private_static :foo
end
Ruby 的元编程非常强大,因此您可以在技术上实现您可能想要的任何范围规则。话虽如此,我还是更喜欢第一个变体的清晰度和minimal surprise。
【讨论】:
【参考方案7】:除非我有误解,否则你不只需要这样的东西吗:
class Foo
private
def Foo.bar
# Complex logic goes here
puts "hi"
end
public
def bar
Foo.bar
end
end
当然,如果您想避免对类名进行硬编码,您可以更改第二个定义以使用您的 self.class.send 方法...
【讨论】:
以上是关于有没有办法从 Ruby 中的实例调用私有类方法?的主要内容,如果未能解决你的问题,请参考以下文章