发布于 2026-01-06 2 阅读
0

向困惑的初学者解释 Ruby 的单例类(Eigenclass) 什么是 Eigenclass?单例模式 Eigenclass class << self 表示法 如果您正在使用类方法,那么您实际上就是在使用 Eigenclass 由 Mux 呈现的 DEV 全球展示挑战赛:展示您的项目!

向困惑的初学者解释 Ruby 的单例类(特征类)

什么是特征级?

辛格尔顿模式

特征类

类 << 自身表示法

如果你使用类方法,实际上就是在使用特征类。

由 Mux 主办的 DEV 全球展示挑战赛:展示你的项目!

本文原文发布于我的博客

当我开始查找特征类究竟是什么时,我感到非常困惑。这种困惑并非源于特征类本身的复杂性,而是因为零星的解释不够清晰,而且没有文章能够全面阐述其概念。

所以我的目标是通过解释 Ruby 的 Eigenclass 究竟是什么以及为什么它被称为“单例类”来帮助大家理解。

要理解特征类,你只需要掌握类和实例的基本概念。温馨提示:

  • 类是我们用来定义对象的工具。
  • 类的实例是由我们的类创建的对象。
class MyClass
  # Body of class
end

MyClass # => class
MyClass.new # => Instance of MyClass
Enter fullscreen mode Exit fullscreen mode

什么是特征级?

这是我最难找到的信息:Eigenclass 被称为 Singleton 类,因为它是一个遵循 Singleton 模式的类。

辛格尔顿模式

单例模式是一种面向对象编程模式,它确保某个类只有一个实例。

Ruby 使用模块来实现单例模式:只需Include Singleton在类定义中写入即可。

这个模块实际上隐藏了该:new方法,MySingletonObject.new否则总是会报错。取而代之的是,它会提供一个实例方法,该方法始终返回类的同一个唯一实例。

require 'singleton'

class NotSingleton
  # 'initialize' is called everytime an instance is created
  def initialize
    puts 'This will be printed many times'
  end
end

class MySingleton
  include Singleton

  # 'initialize' is called everytime an instance is created
  def initialize
    puts 'This will be printed once'
  end
end

NotSingleton.new # => 'This will be printed many times' 
NotSingleton.new # => 'This will be printed many times' 
NotSingleton.new # => 'This will be printed many times' 

MySingleton.instance # => 'This will be printed once'
MySingleton.instance # Nothing is printed
MySingleton.instance # Nothing is printed
Enter fullscreen mode Exit fullscreen mode

如果你想要限制一个类,使其永远只能创建自身的一个实例,那么这将非常有用。

特征类

当你创建一个类的实例时,Ruby 会创建一个隐藏类,它本质上是原类的副本,但只属于当前实例。这就是特征类(Eigenclass)。如果你修改了第一个实例的特征类,它不会影响其他实例。

# This class is NOT a singleton
class ExampleObject
  def print_hello
    puts 'Hello'
  end
end

object1 = ExampleObject.new
object2 = ExampleObject.new

object1.print_hello # => 'Hello'
object2.print_hello # => 'Hello'

def object2.print_hello
  puts 'Bonjour'
end

object1.print_hello # => 'Hello'
object2.print_hello # => 'Bonjour'
Enter fullscreen mode Exit fullscreen mode

搞定了!对象 object2 并非定义在 ExampleObject 类的作用域内,而是由它携带的该类的副本定义。因此,通过在 object2 中重新定义一个方法,我们相当于“打开了特征类”,并仅针对该对象修改了属性。

由于特征类只能存在一个实例,因此有时被称为单例类,尽管 ExampleObject 本身并非单例类。只有它的实例的特征类才是单例,因为它们各自都是唯一的。

类 << 自身表示法

前面的例子可以改写成这样:

class ExampleObject
  def print_hello
    puts 'Hello'
  end
end

object1 = ExampleObject.new
object2 = ExampleObject.new

class << object2
  def print_hello
    puts 'Aloha'
  end
end

object1.print_hello # => 'Hello'
object2.print_hello # => 'Aloha'
Enter fullscreen mode Exit fullscreen mode

如您所见,class << object2 语法用于访问 object2 的 Eigenclass。

现在要讲一个巧妙的技巧:你可能知道,在 Ruby 中,类也是对象。在 Ruby 中,一切皆对象!

任何类的类始终都是类。我喜欢 Ruby 的这一点:它看似毫无道理,但实际上却自有其道理。

你可以自己尝试一下:

# We can create ExampleObject like this :
class ExampleObject
end

# Or like this :
ExampleObject = Class.new

ExampleObject.new.class # => ExampleObject
ExampleObject.class # => Class
Class.class # => Class
ExampleObject.class.class.class.class # => Class
Enter fullscreen mode Exit fullscreen mode

所以,我们刚才看到 ExampleObject 类也是一个对象,更准确地说,它是 Class 类的一个实例。这必然意味着它也有自己的特征类!

如果类“ExampleObject”的实例的特征类是类“ExampleObject”的某种副本,那么类“ExampleObject”的特征类必然是类“Class”的某种副本。

# Instance of class ExampleObject
ExampleObject.new # => Eigenclass : a copy of ExampleObject

# Instance of class Class
ExampleObject # => Eigenclass : a copy of Class
Enter fullscreen mode Exit fullscreen mode

现在,假设我们要访问 ExampleObject 类的 Eigenclass。

我们可以这样做:

class ExampleObject
  # Either inside the definition of the class
  class << self
    def class_print_hello
      puts 'Hello'
    end
  end
end

ExampleObject.class_print_hello # => 'Hello'

# or outside

class << ExampleObject
  def class_print_hello
    puts 'Sayonara'
  end
end

ExampleObject.class_print_hello # => 'Sayonara'
Enter fullscreen mode Exit fullscreen mode

如您所见,我们并非在类的实例上调用该方法(ExampleObject.new),而是在类本身上调用该方法(ExampleObject)。但这没关系,因为 ExampleObject 本身就是 Class 的一个实例。

如果你使用类方法,实际上就是在使用特征类。

如果你使用 Rails,你的模型中通常会包含以下内容:

class MyModel
  def self.print_hello
    puts 'Hello'
  end
end

MyModel.print_hello # => 'Hello'
Enter fullscreen mode Exit fullscreen mode

如您所见,在类定义中定义 self 对象的方法与使用 class << self 打开 Eigenclass 完全相同。

`self.printHello` 也被称为类方法,以区别于实例方法。请参见以下内容:

class MyModel
  # Class Method
  def self.print_hello
    puts 'Hello'
  end

  # Instance Method
  def print_bonjour
    puts 'Bonjour'
  end
end

MyModel.print_hello # => 'Hello'
MyModel.new.print_hello # => ERROR
MyModel.print_bonjour # => ERROR
MyModel.new.print_bonjour # => 'Bonjour'
Enter fullscreen mode Exit fullscreen mode

所以你可以看到,你类的 Class 方法实际上只是你类的 Eigenclass 的一个 Instance 方法。

不要犹豫,把最后那句话重复给公司的实习生们听:他们会认为你是 Ruby 天才!

文章来源:https://dev.to/samuelfaure/explaining-ruby-s-singleton-class-eigenclass-to-confused-beginners-cep