2017-07-10 31 views
4

我想创建一个具有私有类方法的类。我希望可以在实例方法内使用此私有类方法。在Ruby中,`&method(:method_ name)是什么意思?

以下是我第一次尝试:

class Animal 
    class << self 
    def public_class_greeter(name) 
     private_class_greeter(name) 
    end 

    private 
    def private_class_greeter(name) 
     puts "#{name} greets private class method" 
    end 
    end 

    def public_instance_greeter(name) 
    self.class.private_class_greeter(name) 
    end 
end 

Animal.public_class_greeter('John')做工精细,印刷John greets private class method

但是,Animal.new.public_instance_greeter("John")将引发错误:NoMethodError: private method 'private_class_greeter' called for Animal:Class

这是预期的,因为调用与Animal.private_class_greeter相同,这显然会引发错误。

这是如何可以是固定的搜索后,我想出了下面的代码,完成该工作:

class Animal 
    class << self 
    def public_class_greeter(name) 
     private_class_greeter(name) 
    end 

    private 
    def private_class_greeter(name) 
     puts "#{name} greets private class method" 
    end 
    end 

    define_method :public_instance_greeter, &method(:private_class_greeter) 
end 

我完全不明白这里发生了什么:&method(:private_class_greeter)

请问您能解释一下这是什么意思?

如果我是来取代:

define_method :public_instance_greeter, &method(:private_class_greeter) 

有:

def public_instance_greeter 
    XYZ 
end 

那么,究竟应该在的地方XYZ的内容?

回答

8

Ruby如何解析&method(:private_class_greeter)

表达&method(:private_class_greeter)

  • 方法调用method(:private_class_greeter)
  • &操作者前缀的值。

什么是method方法呢?

method方法在当前上下文中查找指定的方法名称并返回一个表示它的对象Method。例如,在irb

def foo 
    "bar" 
end 

my_method = method(:foo) 
#=> #<Method: Object#foo> 

一旦你有了这个方法,你可以做各种事情吧:

my_method.call 
#=> "bar" 

my_method.source_location # gives you the file and line the method was defined on 
#=> ["(irb)", 5] 

# etc. 

什么是&运营商?

&运算符用于传递Proc作为一个块的方法期望一个块要被传递给它。它还隐式地调用您传入的值的to_proc方法,以便将不是Proc的值转换为Proc

Method class implements to_proc —它将方法的内容返回为Proc。因此,您可以用&前缀一个Method实例,并把它作为一个块到另一种方法:

def call_block 
    yield 
end 

call_block &my_method # same as `call_block &my_method.to_proc` 
#=> "bar" 

define_method方法正好拿块与正在定义的新方法的内容。在你的例子中,&method(:private_class_greeter)作为一个块传递现有的private_class_greeter方法。


请问这是&:symbol的工作原理吗?

是的。 Symbol工具to_proc,这样可以简化你的代码是这样的:

["foo", "bar"].map(&:upcase) 
#=> ["FOO", "BAR"] 

# this is equivalent to: 
["foo", "bar"].map { |item| item.upcase } 

# because 
:upcase.to_proc 

# returns this proc: 
Proc { |val| val.send(:upcase) } 

我怎么能复制&method(:private_class_greeter)

您可以在调用对象方法的块传递:

define_method :public_instance_greeter do |name| 
    self.class.send(:private_class_greeter, name) 
end 

当然,那么你就需要使用define_method了,这导致了埃里克his answer提到的相同的解决方案:

def public_instance_greeter(name) 
    self.class.send(:private_class_greeter, name) 
end 
+1

奈斯利解释。 –

+2

'&'**不会将'Method'实例作为一个块传递。它根本不会根据类型区分参数。它在接收方隐含地调用'to_proc'(在这种情况下为''Method''),并将结果('Proc'实例)传递给方法。 '::to_s'完全和':to_s'符号一样调用'Symbol#to_proc'并且进一步传递'Proc'。 – mudasobwa

+0

我正在简化目的的解释。但我想我可以添加一些关于'to_proc'的解释...... –

3

首先,小心你的缩进。 private应该是右边2个空格:它给人的印象是public_instance_greeter否则是私人的。

如果你不关心封装,你可以简单地使用Kernel#send

class Animal 
    class << self 
    def public_class_greeter(name) 
     private_class_greeter(name) 
    end 

    private 
    def private_class_greeter(name) 
     puts "#{name} greets private class method" 
    end 
    end 

    def public_instance_greeter(name) 
    self.class.send(:private_class_greeter, name) 
    end 
end 

Animal.public_class_greeter('John') 
# John greets private class method 
Animal.new.public_instance_greeter("John") 
# John greets private class method 
+1

感谢您对缩进的建议。我知道流行的风格指南。我认为负面缩进“private”关键字有助于轻松识别私有方法的起始位置。这就是为什么我更喜欢将'private'关键字缩进到根级别的原因。 我知道这是针对大多数的风格指南。你认为我的思维过程有意义吗? –

+1

@SatyaramBV:如果你(和你的同事)可以阅读它,那对我来说很好。避免这个问题的一种方法是将'class << self'移动到'Animal'类的末尾。 –

+1

@SatyaramBV FWIW,我所从事的所有团队都喜欢可缩进的可访问性关键字。 – coreyward

相关问题