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.

