Javier Valencia Javier Valencia
Comparando el estilo clásico OOP con el estilo Ruby-idiomático

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

Javier Valencia · · 1 min de lectura · 1257 visitas · Desarrollo
ruby arquitectura patrones funcional oop

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)

Comparando el estilo “clásico orientado a clases” con el estilo Ruby-idiom�

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

Comparando el estilo “clásico orientado a clases” con el estilo R

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)

Comparando el estilo “clásico orientado a clases” con el estilo R

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.