Por qué empiezas a valorar la Programación Orientada a Objetos… cuando todo se rompe

Por qué empiezas a valorar la Programación Orientada a Objetos… cuando todo se rompe

Muchos desarrolladores no entienden realmente la importancia de la programación orientada a objetos… hasta que las cosas empiezan a fallar.

Al inicio de un proyecto, todo es sencillo. Escribes algunas funciones, conectas lógica aquí y allá, y todo funciona. Te sientes rápido, productivo, incluso brillante.

Pero ese momento no dura.

A medida que el proyecto crece, ese mismo código que parecía eficiente empieza a volverse un problema.

// todo mezclado
function processOrder(order) {
  // calcular total
  let total = 0;
  order.items.forEach(item => {
    total += item.price * item.qty;
  });

  // aplicar descuento
  if (order.user.isPremium) {
    total *= 0.9;
  }

  // guardar en base de datos
  saveOrderToDB(order, total);

  // enviar email
  sendEmail(order.user.email, total);

  return total;
}

 

Al inicio esto funciona perfecto.

Pero luego quieres:

  • agregar impuestos
  • Cambiar reglas de descuento
  • Reutilizar lógica en otro flujo
  • Probar el cálculo sin enviar emails

Y todo empieza a romperse.

class OrderCalculator {
  calculate(items) {
    return items.reduce((total, item) => {
      return total + item.price * item.qty;
    }, 0);
  }
}

class DiscountService {
  apply(user, total) {
    if (user.isPremium) {
      return total * 0.9;
    }
    return total;
  }
}

class OrderService {
  constructor() {
    this.calculator = new OrderCalculator();
    this.discount = new DiscountService();
  }

  process(order) {
    let total = this.calculator.calculate(order.items);
    total = this.discount.apply(order.user, total);

    saveOrderToDB(order, total);
    sendEmail(order.user.email, total);

    return total;
  }
}

 

Ahora:

  • Cada clase tiene una responsabilidad clara
  • Puedes cambiar descuentos sin tocar el cálculo
  • Puedes testear cada parte por separado
  • Encapsulación: proteger tu lógica

Antes:

user.balance = user.balance - 100; // cualquiera puede romper esto
class User {
  constructor(balance) {
    this.balance = balance;
  }

  deduct(amount) {
    if (amount > this.balance) {
      throw new Error("Fondos insuficientes");
    }

    this.balance -= amount;
  }
}

Ahora nadie puede modificar el balance sin pasar por reglas.

Evitar duplicación (composición)

Antes:

function sendWelcomeEmail(user) {
  // lógica email
}

function sendOrderEmail(user) {
  // misma lógica repetida
}

Después :

class EmailService {
  send(to, message) {
    // lógica centralizada
  }
}

class UserService {
  constructor() {
    this.email = new EmailService();
  }

  welcome(user) {
    this.email.send(user.email, "Bienvenido");
  }
}

Polimorfismo: flexibilidad real

class PaymentMethod {
  pay(amount) {}
}

class CardPayment extends PaymentMethod {
  pay(amount) {
    console.log("Pagando con tarjeta:", amount);
  }
}

class PaypalPayment extends PaymentMethod {
  pay(amount) {
    console.log("Pagando con PayPal:", amount);
  }
}

Uso :

function processPayment(method) {
  method.pay(100);
}

processPayment(new CardPayment());
processPayment(new PaypalPayment());

La diferencia real

Depurar esto:

processOrder(order);

vs depurar esto :

orderService.process(order);

donde puedes entrar paso a paso en:

cálculo
descuento
persistencia

No es lo mismo.

La programación orientada a objetos no se trata de hacer código elegante.

Se trata de:

  • No perder el control cuando el proyecto crece
  • Saber exactamente dónde está cada cosa
  • Reducir errores invisibles
  • Poder escalar sin romper todo

Y sobre todo…

de no odiar tu propio código dentro de 3 meses.

Comentarios

Aún no hay comentarios. ¿Por qué no comienzas el debate?

Deja una respuesta

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