Comparando el estilo “clásico orientado a clases” con el estilo Ruby-idiomático, funcional y modular

Vamos a aterrizar la idea de Dave Thomas con código Ruby real, comparando el estilo “clásico orientado a clases” con el estilo Ruby-idiomático, funcional y modular, y viendo por qué el segundo suele envejecer mejor.

No es una religión. Es ingeniería pragmática.


1. El punto de partida clásico (el reflejo Java)

Imagina un caso típico: procesar un pedido.

Enfoque habitual con clases

class Order
  attr_reader :items, :customer

  def initialize(items:, customer:)
    @items = items
    @customer = customer
  end

  def total_price
    items.sum(&:price)
  end

  def valid?
    items.any? && customer.active?
  end
end

class OrderProcessor
  def initialize(order)
    @order = order
  end

  def process
    raise "Invalid order" unless @order.valid?

    charge_customer
    send_confirmation
  end

  private

  def charge_customer
    PaymentGateway.charge(@order.customer, @order.total_price)
  end

  def send_confirmation
    Mailer.order_confirmation(@order)
  end
end

Esto es correcto. También es más estructura de la necesaria.

Problemas sutiles:

  • Las clases no modelan cosas del mundo real, sino pasos de un flujo
  • La lógica está dispersa
  • Probar OrderProcessor implica instanciar Order
  • La clase existe solo para agrupar métodos

2. La propuesta de Dave Thomas: empieza por acciones

Ruby no te obliga a empezar pensando en “objetos”.
Puedes empezar pensando en verbos.

Enfoque funcional y plano

def total_price(items)
  items.sum(&:price)
end

def valid_order?(items, customer)
  items.any? && customer.active?
end

def process_order(items:, customer:)
  raise "Invalid order" unless valid_order?(items, customer)

  PaymentGateway.charge(customer, total_price(items))
  Mailer.order_confirmation(items, customer)
end

Observa algo importante:

  • No hay estado implícito
  • Cada función hace una cosa
  • Las dependencias son explícitas
  • Es trivial testear cada función

Esto ya es Ruby de primera clase, no un atajo.


3. “Pero esto queda desordenado”: módulos al rescate

Aquí es donde mucha gente se pone nerviosa.
Dave Thomas dice: no saltes a clases, usa módulos.

module Orders
  module Pricing
    def self.total(items)
      items.sum(&:price)
    end
  end

  module Validation
    def self.valid?(items, customer)
      items.any? && customer.active?
    end
  end

  module Processing
    def self.process(items:, customer:)
      raise "Invalid order" unless Validation.valid?(items, customer)

      PaymentGateway.charge(customer, Pricing.total(items))
      Mailer.order_confirmation(items, customer)
    end
  end
end

Esto aporta:

  • Namespacing claro
  • Ningún estado oculto
  • Ninguna jerarquía artificial
  • Código legible como un mapa mental del dominio

4. ¿Cuándo sí aparece una clase?

Dave Thomas no es anti-clases.
Las clases aparecen cuando hay identidad y estado duradero.

Ejemplo: un Money, un User, un Session.

class Money
  attr_reader :amount, :currency

  def initialize(amount, currency)
    @amount = amount
    @currency = currency
  end

  def +(other)
    raise "Currency mismatch" unless currency == other.currency
    Money.new(amount + other.amount, currency)
  end
end

Aquí la clase tiene sentido porque:

  • Tiene identidad
  • Encapsula invariantes
  • Protege reglas internas

Lo que Dave Thomas critica es crear clases solo para colgar métodos.


5. Un ejemplo muy Rails-real (Service Objects)

El patrón clásico Rails:

class CreateUser
  def initialize(params)
    @params = params
  end

  def call
    user = User.new(@params)
    user.save!
    Mailer.welcome(user)
    user
  end
end

La versión “Ruby puro”:

module Users
  def self.create(params)
    user = User.create!(params)
    Mailer.welcome(user)
    user
  end
end

Pregunta incómoda:
👉 ¿qué aporta realmente la clase CreateUser?

Respuesta honesta: nada, salvo ceremonia.


6. Beneficios reales (no filosóficos)

Después de años, este estilo suele ganar porque:

  • El código crece horizontalmente, no en jerarquías
  • Refactorizar es más fácil
  • Las dependencias están a la vista
  • Los tests no requieren dobles complejos
  • El dominio se expresa como lenguaje, no como UML

Esto es muy Ruby y muy Pragmatic Programmer.


7. La idea profunda del vídeo (la que no sale en el código)

La charla no va de clases.
Va de esto:

No diseñes por anticipación la forma final del sistema.
Deja que la estructura emerja del uso real.

Las clases son una decisión tardía, no el punto de partida.

Comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *