2017-07-10 30 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

相關問題