Nivel inicial2 min de lectura · Publicado el

Métodos de clase en Ruby: self.metodo y class << self

Métodos de clase en Ruby: el molde class Forma, con self.cuadrado y self.triangulo, fabrica galletas; cuadrado.lados lo sabe cada galleta. En la clase, no en el objetorubentejera.com class Formaself.cuadradoself.trianguloForma.cuadrado(5)cuadrado.lados → 4lo hace el moldelo sabe la galleta
El molde es la clase Forma: sus métodos de clase, self.cuadrado y self.triangulo, fabrican galletas y se llaman sobre la clase. Cada galleta es un objeto y sabe lo suyo: cuadrado.lados da 4.

Una clase es como el molde de una fábrica de galletas, y los objetos son las galletas. Los métodos de instancia los tiene cada galleta (galleta.comer). Los métodos de clase los tiene el molde: sirven para cosas que no dependen de una galleta concreta, como fabricar una de un tipo determinado (Galleta.de_chocolate).

Si todavía no has visto clases, empieza por Clases en Ruby.

Tres formas de definir un método de clase

class Demo
  def Demo.metodo1      # con el nombre de la clase
    "metodo1"
  end

  def self.metodo2      # con self: la forma más habitual
    "metodo2"
  end

  class << self         # un bloque para varios métodos de clase
    def metodo3
      "metodo3"
    end
  end
end

Demo.metodo1   # "metodo1"
Demo.metodo2   # "metodo2"
Demo.metodo3   # "metodo3"

Dentro de la definición de una clase, self es la propia clase, así que def self.metodo2 equivale a def Demo.metodo2. Se prefiere self porque, si renombras la clase, no tienes que tocar cada método.

Un método de clase no está disponible en los objetos:

Demo.new.metodo2
# NoMethodError: undefined method 'metodo2' for an instance of Demo
Forma Cuándo usarla
def self.nombre La opción por defecto
def Demo.nombre Funciona, pero repite el nombre de la clase
class << self Cuando hay varios métodos de clase o quieres usar attr_reader o private para ellos

Métodos de clase como constructores con nombre

new crea objetos, pero no dice qué tipo de objeto quieres. Un método de clase le pone nombre a la intención (a esto también se le llama método fábrica o pseudo-constructor):

class Forma
  attr_reader :lados, :perimetro

  def initialize(lados, perimetro)
    @lados = lados
    @perimetro = perimetro
  end

  def self.cuadrado(lado)
    new(4, lado * 4)
  end

  def self.triangulo(lado)
    new(3, lado * 3)
  end
end

cuadrado = Forma.cuadrado(5)
p [cuadrado.lados, cuadrado.perimetro]   # [4, 20]

Dentro del método de clase basta con new: ya estás en la clase, no hace falta escribir Forma.new.

Forma.cuadrado(5) se entiende mejor que Forma.new(4, 20).

class << self con atributos de la clase

class << self es útil cuando la clase guarda datos propios. Aquí la clase cuenta cuántos usuarios ha creado:

class Usuario
  @total = 0

  class << self
    attr_reader :total

    def crear(nombre)
      @total += 1
      new
    end
  end
end

Usuario.crear("Ana")
Usuario.crear("Luis")
p Usuario.total   # 2

Para guardar datos a nivel de clase hay varias opciones, con matices; los tienes en Variables de clase en Ruby.

Fuentes

Todos los ejemplos se han ejecutado con Ruby 4.0.9 (imagen oficial ruby:latest de Docker) y las salidas de los comentarios son las reales.