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
OrderProcessorimplica instanciarOrder - 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.