单例方法和类方法的区别

14

类方法和单例方法是做同样的事情还是不同的?以下是一个示例。

class C
  def self.classmethod
    puts "class method #{self}"
  end
end

C.classmethod  # class method C
c = C.new

def c.singletonmethod
  puts "instance method #{self}"
end

c.singletonmethod  # instance method #<C:0x0000000118ed08>
4个回答

23

大多数 Ruby 中发生的事情涉及到类和模块,其中包含实例方法的定义。

class C
  def talk
    puts "Hi!"
  end
end

c = C.new
c.talk
Output: Hi!

但是正如您之前看到的(甚至早于您在类中看到实例方法),您还可以直接在单个对象上定义单例方法:

obj = Object.new
def obj.talk
  puts "Hi!"
end
obj.talk
#Output: Hi!

当您在给定对象上定义一个单例方法时,只有该对象才能调用该方法。正如您所见,最常见的单例方法类型是类方法 - 即逐个添加到Class对象的方法:

class Car
  def self.makes
    %w{ Honda Ford Toyota Chevrolet Volvo }
  end
end
但是任何对象都可以添加单例方法。在每个对象上定义基于方法的行为的能力是Ruby设计的特点之一。
单例类
单例类是匿名的:虽然它们是类对象(类Class的实例),但它们会自动出现而不需要给定名称。尽管如此,您可以打开单例类的类定义体并向其添加实例方法、类方法和常量,就像处理普通类一样。
注意:
每个对象都有两个类:
■ 它是一个实例的类
■ 其单例类
----------------------------------------------------------------
最后,我极力推荐你观看:
1:Ruby对象模型和元编程,了解单例方法与类方法的详细信息
2:元编程-扩展Ruby的乐趣和利润 - Dave Thomas 演讲
希望这可以帮助你!

1
超棒的解释!我很激动。 - user4932805

23

没有区别。在 Ruby 中不存在类方法。“类方法”只是我们人类给单例方法在对象恰好是 Class 类实例时所起的名字。


6
在Ruby中,类也是一个对象。因此,在你的例子中,classmethod是对象C的单例方法,而singletonmethod是对象c的单例方法。
我强烈推荐保罗·佩罗塔(Paolo Perrotta)的书《Ruby元编程》。

3

由于所有方法都是通过类定义实现和存储的,因此对象定义其自己的方法应该是不可能的。然而,Ruby提供了一种解决方案-您可以定义仅适用于特定对象的方法。这些方法称为Singleton方法。

class Instance
  def instance_method
    puts "instance method called"
  end
end

in_obj = Instance.new

上面的语句给出了一个Instance类的参考对象,你可能会得到一个不同的id。
#<Instance:0x00000000e9ee68>

让我们使用对象调用实例方法并获取输出结果。

in_obj.instance_method
instance method called

现在,创建单例方法。
sin_obj = Instance.new
#<Instance:0x00000000e98d60>
def sin_obj.singleton_method
  puts "singleton method called"
end

调用单例方法

sin_obj.sigleton_method
singleton method called

在此,对象sin_obj具有单例方法singleton_method。这是一个单例方法,因为它属于类的一个实例,并且不会与其他实例共享。
然而,单例方法与实例对象不能持有方法的观点相矛盾,只有类定义(类Class的对象)可以。事实上介于两者之间。当您在对象上声明单例方法时,Ruby会自动创建一个仅容纳该方法的类。这个类被称为对象的“元类”。此后该对象的所有单例方法都归于其元类。每当您向该对象发送消息时,它首先查看其元类是否存在该方法。如果不存在,则将其传播到对象的实际类中,如果也未找到,则消息遍历继承层次结构。让我们在同一Instance类中定义类方法。现在,让我们用一个例子来检查我上面说的事实。
foo = "Hey I am a string"
def foo.foo_instance_method
    p self.upcase
end

验证单例模式定义是否进入元类。

p foo.class.instance_methods.include?:foo_instance_method
 false
p foo.metaclass.instance_methods.include?:foo_instance_method
 true
p foo.metaclass.class
 Class
p foo.metaclass
 #<Class:#<String:0x00000000e8f0f8>>

让我们来看一下元类是如何实现的。
class Instance
  def metaclass
    class << self
      self
    end
  end
end

类 << self 语法将当前的self更改为指向当前对象的元类。由于我们已经在实例方法内部,这将是实例的元类。该方法只是简单的返回此时的self,它是实例的元类。

元类在许多方面几乎像一个类。但是它不能被实例化。请参见下面的示例以进行检查。

a = Instance.new
a.metaclass.new
You will get is error when try to intantiate the meta class.
*TypeError: can't create instance of singleton class*

在Ruby中,“metaclass”和“singleton class”是相同的。在Ruby文献中,你会发现它们互换使用。
class Instance
    def self.class_method #instead one can use def Instance.class_method
        puts "class method called"        
    end
end

然而,也可以按照这种语法来定义一个类方法。
class Instance
  class << self
    def show
      puts "Class method show invoked"
    end
  end
end

调用 class_method 方法

Instance.class_method
class method called
Instance.show 
Class method show invoked

它由为当前对象(self)构建一个新的匿名类组成。要创建匿名类,我们使用<<符号。仔细查看类方法和单例方法的定义,你会发现它们之间的区别。 考虑以下示例。

class Instance 
 puts self
 def meth
  p self
 end
end

在这里,meth方法外的self只是类,而在instance_method中,self指向当前实例或对象。

此外,您还可以检查方法是否响应特定对象。

p Instance.new.respond_to?(:singleton_method)
p Instance.new.respond_to?(:class_method)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接