Saltar al contenido
Explorar conocimiento

concepto · fundamentos · 10 min de lectura

Programación Orientada a Objetos (POO)

La POO organiza el código alrededor de objetos que contienen datos y comportamiento, usando encapsulación, abstracción, herencia y polimorfismo para gestionar complejidad.

No requiere conocimientos previos.

Alcance en breve

Cubre

  • the four pillars of object-oriented programming as a paradigm
  • encapsulation, abstraction, inheritance, and polymorphism at a conceptual level
  • the difference between OOP and other paradigms such as procedural and functional programming
  • classes and objects as the core building blocks of OOP
  • SOLID principles as a natural extension of OOP design discipline

No cubre

  • in-depth treatment of individual pillars (each has its own node)
  • specific design patterns (covered in dedicated pattern nodes)
  • language-specific OOP implementation details beyond conceptual examples

Supone

  • The reader has basic programming literacy and has written or read code in at least one language.

Resumen

POO (Programación Orientada a Objetos) es un paradigma de programación que organiza el código alrededor de objetos: entidades que agrupan estado (datos) y comportamiento (métodos) en una misma unidad. En vez de separar datos y funciones como hace la programación procedural, la POO los reúne porque pertenecen al mismo concepto del dominio.

Importa porque la mayoría de los sistemas de software reflejan entidades del mundo real o del negocio: usuarios, pedidos, cuentas, dispositivos. La POO ofrece un lenguaje de diseño que se alinea con esa realidad. Sus cuatro pilares —encapsulación, abstracción, herencia y polimorfismo— son herramientas para gestionar complejidad, no adornos académicos. Aplicados bien, producen código que se puede leer, probar y modificar sin romper lo que ya funciona.

No toda solución necesita POO. Un script corto, una transformación de datos o una función matemática pueden ser más claros sin objetos. Pero cuando un sistema crece y distintas partes necesitan interactuar con los mismos datos bajo reglas consistentes, la POO da un marco para tomar esas decisiones de diseño de forma explícita.

Alcance y supuestos

Este paquete cubre el paradigma de POO como un todo: qué lo define, cuáles son sus cuatro pilares, cómo se diferencia de otros paradigmas y cuándo conviene aplicarlo. También introduce los principios SOLID como la disciplina de diseño que naturalmente sigue a una POO bien aplicada.

No cubre en profundidad cada pilar individual (encapsulación, herencia, polimorfismo y abstracción tienen sus propios nodos). Tampoco cubre patrones de diseño concretos ni detalles de implementación específicos de cada lenguaje más allá de ejemplos conceptuales en TypeScript.

Asume que la persona lectora tiene experiencia básica de programación: entiende variables, funciones, y ha escrito o leído código en al menos un lenguaje.

Modelo mental

Piensa en una empresa con empleados. Cada empleado tiene datos —nombre, cargo, salario— y puede ejecutar ciertas acciones —registrar horas, pedir vacaciones, transferir un expediente—. Esos datos y acciones forman una unidad: no le pedirías a Recursos Humanos que calcule la nómina mirando una planilla suelta de nombres y otra separada de salarios. Querés que cada empleado sea una entidad que sabe responder preguntas sobre sí mismo.

La POO aplica ese mismo principio al código. Un objeto Employee contiene sus propios datos (name, salary) y sus propios métodos (requestVacation, calculateBonus). La lógica que modifica ese estado vive dentro del objeto, no dispersa en funciones sueltas.

El paradigma procedural opuesto separaría datos en structs o registros y funciones en módulos aparte. Eso funciona para problemas chicos, pero cuando veinte funciones distintas tocan los mismos datos, el programador debe recordar qué función modifica qué campo y en qué orden. La POO reduce esa carga: el objeto es dueño de sus datos, y solo sus métodos los modifican.

No es magia ni dogma: es una decisión de diseño que explicita dónde vive cada responsabilidad. Los cuatro pilares —encapsulación, abstracción, herencia y polimorfismo— son los mecanismos concretos con los que implementás esa decisión.

Uso práctico

Usa POO cuando modelás entidades con identidad, estado propio y reglas que conviene mantener cerca de ese estado:

  • ✅ Un ShoppingCart que protege sus ítems y su total con métodos como addItem y checkout.
  • ✅ Una jerarquía PaymentMethod → CreditCard, BankTransfer donde cada variante sabe procesarse distinto.
  • ✅ Un User con changeEmail que valida el nuevo email antes de reemplazarlo.
  • ❌ Un script que lee un CSV, lo transforma y escribe un JSON probablemente no necesita objetos.
  • ❌ Una API que solo recibe datos y los guarda tal cual sin lógica de negocio puede usar funciones simples.

Ejemplo trabajado: modelar un sistema de pagos con los cuatro pilares

Los cuatro pilares de POO no se entienden aislados: se entienden viendo cómo colaboran en un mismo diseño. El siguiente ejemplo modela un sistema de pagos donde cada pilar resuelve una parte distinta del problema.

1. Encapsulación

Esconder los detalles internos de un objeto y exponer solo lo necesario. No es solo poner atributos private: es decidir qué puede cambiar sin que el código externo se rompa.

class BankAccount {
  private balance: number = 0;

  deposit(amount: number) {
    if (amount <= 0) throw new Error("Amount must be positive");
    this.balance += amount;
  }

  getBalance() {
    return this.balance;
  }
}

Nadie fuera de BankAccount puede modificar balance directamente. Solo deposit y cualquier otro método que definas pueden hacerlo. Si mañana cambiás balance: number por balanceInCents: number, el resto del código no se entera.

2. Abstracción

Mostrar qué hace un objeto sin exponer cómo lo hace. La abstracción responde a "¿qué necesita saber quien usa este objeto?" y oculta todo lo demás.

interface NotificationService {
  send(recipient: string, message: string): void;
}

class EmailNotification implements NotificationService {
  send(recipient: string, message: string) {
    // conecta al servidor SMTP, arma headers, maneja reintentos...
    console.log(`Email sent to ${recipient}: ${message}`);
  }
}

Quien usa NotificationService solo necesita saber que tiene un método send. No necesita saber si el envío es por email, SMS o push. Eso permite cambiar la implementación sin tocar el código que la consume.

3. Herencia

Crear una clase nueva a partir de una existente, heredando su estructura y comportamiento. La subclase puede especializar o extender lo que recibe.

class Vehicle {
  constructor(protected speed: number) {}

  move() {
    return `Moving at ${this.speed} km/h`;
  }
}

class Car extends Vehicle {
  constructor(speed: number, private brand: string) {
    super(speed);
  }

  honk() {
    return `${this.brand} says beep!`;
  }
}

Car hereda move() de Vehicle sin reescribirlo y agrega honk(). La herencia permite reutilizar código cuando la relación es genuinamente "es un". El error clásico es usar herencia cuando la relación es "usa un" o "tiene un": un Car no debería heredar de Engine; debería componerlo como atributo.

La comunidad de POO moderna prefiere composición sobre herencia para la mayoría de los casos: en vez de heredar comportamiento, un objeto recibe sus dependencias desde fuera. Eso produce diseños más flexibles y menos acoplados.

4. Polimorfismo

Permitir que objetos de distintos tipos respondan al mismo mensaje de forma diferente. Es el mecanismo que hace que la abstracción funcione en la práctica.

interface Shape {
  area(): number;
}

class Circle implements Shape {
  constructor(private radius: number) {}
  area() { return Math.PI * this.radius ** 2; }
}

class Rectangle implements Shape {
  constructor(private width: number, private height: number) {}
  area() { return this.width * this.height; }
}

function printArea(shape: Shape) {
  console.log(`Area: ${shape.area()}`);
}

printArea(new Circle(5));      // Area: 78.54
printArea(new Rectangle(4, 6)); // Area: 24

printArea no sabe qué forma recibe. Solo sabe que tiene un método area(). Cada forma calcula su área como corresponde. Podés agregar Triangle mañana sin tocar printArea. Eso es polimorfismo: un mismo contrato, múltiples comportamientos.

Contraste con otros paradigmas

La POO no es el único paradigma, y cada uno brilla en su contexto:

| Aspecto | POO | Procedural | Funcional | |---|---|---|---| | Unidad base | Objeto (datos + métodos) | Procedimiento (función) | Función pura | | Estado | Mutación controlada dentro del objeto | Variables globales o paso explícito | Inmutable, sin efectos laterales | | Reutilización | Herencia y composición | Funciones compartidas | Composición de funciones | | Organización | Clases y jerarquías | Archivos y módulos | Pipelines y transformaciones | | Ideal para | Modelado de entidades con reglas de negocio | Scripts y automatización simple | Transformación de datos y concurrencia |

Los paradigmas no son excluyentes. Un sistema puede usar objetos para el dominio, funciones puras para transformar datos y procedimientos para scripts de despliegue. La POO es una herramienta, no una religión.

SOLID: disciplina de diseño para POO

Los principios SOLID son cinco reglas que guían el diseño de clases en POO. No son el punto de partida, pero sí el termómetro para detectar cuándo un diseño orientado a objetos se está descontrolando:

  • Single Responsibility: una clase debe tener un solo motivo para cambiar.
  • Open/Closed: abierta a extensión, cerrada a modificación.
  • Liskov Substitution: una subclase debe poder reemplazar a su clase base sin romper el programa.
  • Interface Segregation: muchas interfaces específicas son mejores que una interfaz general.
  • Dependency Inversion: depende de abstracciones, no de implementaciones concretas.

Un chequeo rápido antes de diseñar con POO:

  1. ¿El problema tiene entidades con identidad y estado que evoluciona con reglas?
  2. ¿Distintas partes del sistema necesitan interactuar con los mismos datos de forma controlada?
  3. ¿Las variantes de comportamiento (por ejemplo, distintos medios de pago) aparecen naturalmente en el dominio?
  4. ¿El equipo y el ecosistema del lenguaje soportan bien el paradigma orientado a objetos?

Si tres de cuatro respuestas son sí, POO probablemente facilita el diseño. Si el problema es una cadena de transformaciones de datos sin estado compartido, un enfoque funcional puede ser más directo.

Evidencia

Fuentes citadas