Qué es la Programación Orientada a Objetos y sus 4 pilares explicados

D
DanisCh
(Actualizado: ) 18 min de lectura
Qué es la Programación Orientada a Objetos y sus 4 pilares explicados
Empezar desde cero Roadmaps de programación

Hay un momento en el aprendizaje de la programación en el que dejas de escribir código como una lista de instrucciones y empiezas a pensar en términos de entidades con propiedades y comportamientos. Ese momento es cuando descubres la Programación Orientada a Objetos.

La POO es el paradigma dominante en el desarrollo de software moderno. Python, Java, C++, C#, JavaScript — todos lo soportan. Y sus 4 pilares son los conceptos que más aparecen en entrevistas técnicas, exámenes universitarios y proyectos reales.

En este artículo vas a entender qué es la POO, qué son las clases y los objetos, y cada uno de los 4 pilares con el mismo ejemplo evolucionando a lo largo del artículo. Sin saltarte nada.


¿Qué es la Programación Orientada a Objetos?

La Programación Orientada a Objetos (POO, o en inglés OOP: Object-Oriented Programming) es un paradigma de programación que organiza el código en torno a objetos en lugar de funciones y procedimientos sueltos.

Un paradigma es simplemente una forma de pensar y estructurar los programas. Antes de la POO dominaba la programación estructurada o procedural: el código era una secuencia de instrucciones de arriba a abajo. Funcionaba para programas pequeños, pero cuando crecían se convertían en lo que los programadores llaman código espagueti: imposible de mantener y lleno de efectos secundarios inesperados.

La POO propone una solución diferente: modelar el programa como un conjunto de objetos que representan entidades del mundo real (o del dominio del problema) y que colaboran entre sí.

La analogía del mundo real

Mira a tu alrededor. El mundo está lleno de objetos: un coche, un teléfono, una persona, una cuenta bancaria. Cada objeto tiene:

  • 📋 Propiedades — características que lo describen (el coche tiene color, marca, velocidad)
  • Comportamientos — acciones que puede realizar (el coche puede arrancar, acelerar, frenar)

La POO lleva exactamente esta forma de pensar al código:

Mundo real:              POO:
  Objeto: Coche    →     Clase: Coche
  Propiedades      →     Atributos (datos)
  Comportamientos  →     Métodos (funciones)
  Mi coche concreto→     Objeto (instancia de la clase)

Clases y Objetos — los bloques fundamentales

Antes de entrar en los 4 pilares necesitas tener claro la diferencia entre clase y objeto. Es la base de todo.

🏗️ Clase — el molde o plano

Una clase es una plantilla o molde que define qué atributos y métodos tendrán los objetos creados a partir de ella. No es un objeto en sí misma: es la definición de cómo serán los objetos.

Analogía: el plano arquitectónico de una casa. El plano no es una casa, pero define cómo serán todas las casas construidas con ese plano.

📦 Objeto — la instancia concreta

Un objeto (o instancia) es una realización concreta de una clase. Si la clase es el molde, el objeto es la pieza fabricada con ese molde.

# ============================================
# CLASE: el molde / la plantilla
# ============================================

class CuentaBancaria:
    """Representa una cuenta bancaria."""

    def __init__(self, titular, saldo_inicial=0):
        """Constructor — se ejecuta al crear el objeto."""
        self.titular = titular          # atributo
        self.saldo   = saldo_inicial    # atributo

    def depositar(self, monto):         # método
        self.saldo += monto
        print(f"Depósito de ${monto}. Nuevo saldo: ${self.saldo}")

    def consultar_saldo(self):          # método
        return f"{self.titular}: ${self.saldo}"


# ============================================
# OBJETOS: instancias concretas de la clase
# ============================================

cuenta_ana  = CuentaBancaria("Ana García", 1000)   # objeto 1
cuenta_luis = CuentaBancaria("Luis Pérez", 500)    # objeto 2

# Cada objeto tiene sus propios datos, independientes del otro
cuenta_ana.depositar(250)
print(cuenta_ana.consultar_saldo())   # Ana García: $1250
print(cuenta_luis.consultar_saldo())  # Luis Pérez: $500

print(type(cuenta_ana))    # <class '__main__.CuentaBancaria'>

💡 El método __init__ es el constructor: la función especial que se ejecuta automáticamente cuando creas un objeto. self hace referencia al propio objeto que se está creando.


Los 4 pilares de la POO

Los cuatro pilares no son conceptos independientes — trabajan juntos para producir software modular, reutilizable y mantenible. Vamos a ver cada uno con el mismo sistema bancario evolucionando.


🔒 Pilar 1: Encapsulamiento — proteger los datos

¿Qué es?

El encapsulamiento es el principio de combinar datos y métodos en una sola unidad (la clase) y controlar el acceso a esos datos desde el exterior. No todo el mundo puede modificar todo.

La analogía

Imagina un cajero automático. Puedes depositar dinero, retirar dinero y consultar el saldo. Pero no puedes abrir el cajero y modificar directamente el número en la base de datos. Hay una interfaz controlada para interactuar con él.

El encapsulamiento hace lo mismo: expone solo lo necesario y oculta el resto.

Sin encapsulamiento — el problema

# ❌ Sin encapsulamiento: cualquiera puede modificar el saldo directamente
class CuentaSinEncapsular:
    def __init__(self, titular, saldo):
        self.titular = titular
        self.saldo   = saldo    # atributo público → cualquiera lo modifica

cuenta = CuentaSinEncapsular("Ana", 1000)
cuenta.saldo = -999999  # ← alguien puede poner un saldo negativo directamente
cuenta.saldo = "hola"   # ← o incluso un string en lugar de un número
print(cuenta.saldo)     # "hola" — ¡la cuenta está rota!

Con encapsulamiento — la solución

# ✅ Con encapsulamiento: los datos internos están protegidos

class CuentaBancaria:
    def __init__(self, titular, saldo_inicial=0):
        self.titular = titular
        self.__saldo = saldo_inicial  # __ = atributo privado (Python)

    # Getter — forma controlada de leer el saldo
    @property
    def saldo(self):
        return self.__saldo

    # Método controlado para depositar
    def depositar(self, monto):
        if not isinstance(monto, (int, float)):
            raise ValueError("El monto debe ser numérico")
        if monto <= 0:
            raise ValueError("El monto debe ser positivo")
        self.__saldo += monto
        print(f"✅ Depósito: +${monto} | Saldo: ${self.__saldo}")

    # Método controlado para retirar
    def retirar(self, monto):
        if monto <= 0:
            raise ValueError("El monto debe ser positivo")
        if monto > self.__saldo:
            raise ValueError("Saldo insuficiente")
        self.__saldo -= monto
        print(f"✅ Retiro: -${monto} | Saldo: ${self.__saldo}")

    def __str__(self):
        return f"Cuenta de {self.titular} | Saldo: ${self.__saldo}"


cuenta = CuentaBancaria("Ana García", 1000)
cuenta.depositar(500)
cuenta.retirar(200)
print(cuenta)               # Cuenta de Ana García | Saldo: $1300

# Intentar acceder directamente al atributo privado:
# cuenta.__saldo = -999  # ← No funciona como esperarías en Python
print(cuenta.saldo)         # $1300 ← solo a través del getter

Los modificadores de acceso

# En Python, la convención es:
self.nombre      # público — accesible desde cualquier lugar
self._nombre     # protegido — convención: solo clase y subclases (no forzado)
self.__nombre    # privado — Python lo "mangled" a _Clase__nombre

# En Java/C# (más estrictos):
public String nombre;    // accesible desde cualquier lugar
protected String nombre; // accesible desde clase y subclases
private String nombre;   // solo accesible dentro de la clase

💡 Beneficio clave del encapsulamiento: puedes cambiar la implementación interna de la clase sin afectar al código que la usa. Si cambia cómo se calcula el saldo internamente, el código externo no nota la diferencia — sigue usando los mismos métodos.


👨‍👩‍👧 Pilar 2: Herencia — reutilizar y especializar

¿Qué es?

La herencia es el mecanismo que permite crear una nueva clase basada en una clase existente, heredando sus atributos y métodos, y pudiendo añadir o modificar comportamientos específicos.

La analogía

Los seres vivos heredan características de sus progenitores. Un perro hereda de mamífero: tiene los rasgos comunes (respira, tiene sangre caliente, alimenta con leche) más los propios de los perros (ladra, tiene pelaje, etc.). No necesitas redefinir qué es un mamífero cada vez que defines un animal.

En código

# ============================================
# Clase BASE (padre / superclase)
# ============================================

class CuentaBancaria:
    def __init__(self, titular, saldo_inicial=0):
        self.titular  = titular
        self.__saldo  = saldo_inicial
        self._historial = []

    @property
    def saldo(self):
        return self.__saldo

    def depositar(self, monto):
        if monto <= 0:
            raise ValueError("Monto inválido")
        self.__saldo += monto
        self._historial.append(f"Depósito: +${monto}")

    def retirar(self, monto):
        if monto > self.__saldo:
            raise ValueError("Saldo insuficiente")
        self.__saldo -= monto
        self._historial.append(f"Retiro: -${monto}")

    def ver_historial(self):
        return self._historial

    def __str__(self):
        return f"[Cuenta estándar] {self.titular} | Saldo: ${self.saldo}"


# ============================================
# Clases HIJAS (subclases) — heredan de CuentaBancaria
# ============================================

class CuentaAhorros(CuentaBancaria):
    """Cuenta de ahorros con tasa de interés."""

    def __init__(self, titular, saldo_inicial=0, tasa_interes=0.03):
        super().__init__(titular, saldo_inicial)  # llama al constructor del padre
        self.tasa_interes = tasa_interes          # atributo propio

    def aplicar_interes(self):
        """Método nuevo — solo existe en CuentaAhorros."""
        interes = self.saldo * self.tasa_interes
        self.depositar(interes)
        print(f"💰 Interés aplicado: +${interes:.2f}")

    def __str__(self):
        return f"[Cuenta Ahorros] {self.titular} | Saldo: ${self.saldo} | Tasa: {self.tasa_interes*100}%"


class CuentaCorriente(CuentaBancaria):
    """Cuenta corriente con límite de sobregiro."""

    def __init__(self, titular, saldo_inicial=0, limite_sobregiro=500):
        super().__init__(titular, saldo_inicial)
        self.limite_sobregiro = limite_sobregiro  # atributo propio

    def retirar(self, monto):
        """Sobreescribe el método retirar del padre — permite sobregiro."""
        if monto > self.saldo + self.limite_sobregiro:
            raise ValueError("Supera el límite de sobregiro")
        # Llama directamente a la manipulación del saldo
        # (en una implementación real accedería al saldo de otra forma)
        super().retirar(min(monto, self.saldo))  # simplificado para el ejemplo
        print(f"⚡ Retiro con posible sobregiro de ${monto}")

    def __str__(self):
        return f"[Cuenta Corriente] {self.titular} | Saldo: ${self.saldo} | Sobregiro: ${self.limite_sobregiro}"


# ============================================
# Usando las clases
# ============================================

ahorro   = CuentaAhorros("Ana García", 1000, tasa_interes=0.05)
corriente = CuentaCorriente("Luis Pérez", 500, limite_sobregiro=1000)

ahorro.depositar(500)     # heredado del padre ✅
ahorro.aplicar_interes()  # propio de CuentaAhorros ✅
print(ahorro)

corriente.depositar(200)  # heredado del padre ✅
print(corriente)

# isinstance verifica la jerarquía de herencia
print(isinstance(ahorro, CuentaAhorros))    # True
print(isinstance(ahorro, CuentaBancaria))   # True ← ahorro ES TAMBIÉN una CuentaBancaria

Ventajas de la herencia

  • Reutilización — no repites el código común en cada subclase
  • Especialización — cada subclase añade o modifica solo lo necesario
  • Jerarquía — modelas relaciones del mundo real de forma natural
  • Mantenimiento — un cambio en la clase padre se propaga a todas las hijas

🎭 Pilar 3: Polimorfismo — una interfaz, múltiples comportamientos

¿Qué es?

El polimorfismo (del griego: "muchas formas") es la capacidad de que objetos de diferentes clases respondan al mismo mensaje (método) de formas distintas. Puedes tratar objetos de diferentes tipos como si fueran del mismo tipo base, y cada uno se comportará según su propia implementación.

La analogía

El botón "reproducir" hace cosas diferentes en Spotify (reproduce música), en Netflix (reproduce un video) y en una presentación de PowerPoint (inicia la presentación). El mismo "mensaje" (reproducir()), comportamientos distintos según el objeto.

# ============================================
# POLIMORFISMO — misma llamada, diferente resultado
# ============================================

class CuentaBancaria:
    def __init__(self, titular, saldo=0):
        self.titular = titular
        self._saldo  = saldo

    def calcular_comision_mensual(self):
        """Cada tipo de cuenta calcula su comisión diferente."""
        return 0  # la base no cobra comisión

    def resumen(self):
        comision = self.calcular_comision_mensual()
        return f"{self.titular}: Saldo ${self._saldo} | Comisión mensual: ${comision}"


class CuentaAhorros(CuentaBancaria):
    def calcular_comision_mensual(self):
        return 0  # cuentas de ahorro sin comisión


class CuentaCorriente(CuentaBancaria):
    def calcular_comision_mensual(self):
        return 5.99  # comisión fija mensual


class CuentaPremium(CuentaBancaria):
    def __init__(self, titular, saldo=0):
        super().__init__(titular, saldo)

    def calcular_comision_mensual(self):
        # Comisión del 0.1% del saldo, mínimo $2
        return max(self._saldo * 0.001, 2)


# ============================================
# El poder del polimorfismo:
# Una sola función que trabaja con CUALQUIER tipo de cuenta
# ============================================

def procesar_cuentas(lista_cuentas):
    """No necesita saber de qué tipo es cada cuenta."""
    total_comisiones = 0
    for cuenta in lista_cuentas:
        print(cuenta.resumen())              # ← polimorfismo en acción
        total_comisiones += cuenta.calcular_comision_mensual()
    print(f"\nTotal comisiones del mes: ${total_comisiones:.2f}")


# Lista con diferentes tipos de cuentas mezcladas
cuentas = [
    CuentaAhorros("Ana García", 5000),
    CuentaCorriente("Luis Pérez", 2000),
    CuentaPremium("María López", 50000),
    CuentaCorriente("Carlos Ruiz", 800),
]

procesar_cuentas(cuentas)

# Salida:
# Ana García: Saldo $5000  | Comisión mensual: $0
# Luis Pérez: Saldo $2000  | Comisión mensual: $5.99
# María López: Saldo $50000| Comisión mensual: $50.0
# Carlos Ruiz: Saldo $800  | Comisión mensual: $5.99
# Total comisiones del mes: $61.98

Los dos tipos de polimorfismo

# 1. Polimorfismo de SOBREESCRITURA (override) — lo que vimos arriba
#    La subclase redefine un método del padre
class Animal:
    def hablar(self):
        return "..."

class Perro(Animal):
    def hablar(self):       # sobreescribe el método del padre
        return "¡Guau!"

class Gato(Animal):
    def hablar(self):       # sobreescribe el método del padre
        return "¡Miau!"

animales = [Perro(), Gato(), Perro()]
for animal in animales:
    print(animal.hablar())  # Guau! Miau! Guau!


# 2. Polimorfismo de SOBRECARGA (overload) — mismo nombre, distintos parámetros
#    Python no tiene sobrecarga nativa, pero se simula con parámetros opcionales
class Calculadora:
    def sumar(self, a, b, c=0):
        return a + b + c

calc = Calculadora()
print(calc.sumar(5, 3))     # 8
print(calc.sumar(5, 3, 2))  # 10

💡 Por qué el polimorfismo es tan poderoso: la función procesar_cuentas() no sabe ni necesita saber de qué tipo exacto es cada cuenta. Funciona con cualquier objeto que tenga el método calcular_comision_mensual(). Si mañana añades CuentaJubilacion, la función sigue funcionando sin cambiar una sola línea.


🎨 Pilar 4: Abstracción — simplificar la complejidad

¿Qué es?

La abstracción es el proceso de mostrar solo la información esencial de un objeto y ocultar los detalles de implementación irrelevantes para el usuario. Defines qué hace un objeto, no cómo lo hace.

La analogía

Cuando conduces un coche, usas el volante, el acelerador y el freno. No necesitas saber cómo funciona el motor internamente, cómo la gasolina se convierte en movimiento, ni cómo el sistema de frenos transforma la presión en desaceleración. La interfaz de conducción abstrae toda esa complejidad.

Abstracción con clases abstractas

from abc import ABC, abstractmethod

# ============================================
# CLASE ABSTRACTA — define el contrato
# No se puede instanciar directamente
# ============================================

class CuentaBancariaAbstracta(ABC):
    """Define qué métodos DEBEN existir en toda cuenta bancaria."""

    def __init__(self, titular, saldo=0):
        self.titular = titular
        self._saldo  = saldo

    @abstractmethod
    def calcular_interes(self):
        """Cada tipo de cuenta calcula el interés diferente."""
        pass  # sin implementación — las subclases DEBEN implementarlo

    @abstractmethod
    def tipo_cuenta(self):
        """Cada subclase debe decir qué tipo de cuenta es."""
        pass

    # Método concreto — igual para todas las cuentas
    def depositar(self, monto):
        if monto > 0:
            self._saldo += monto

    def resumen_completo(self):
        """Usa los métodos abstractos sin saber su implementación."""
        return (f"Tipo: {self.tipo_cuenta()}\n"
                f"Titular: {self.titular}\n"
                f"Saldo: ${self._saldo}\n"
                f"Interés mensual estimado: ${self.calcular_interes():.2f}")


# ============================================
# CLASES CONCRETAS — implementan el contrato
# ============================================

class CuentaAhorros(CuentaBancariaAbstracta):
    def tipo_cuenta(self):
        return "Cuenta de Ahorros"

    def calcular_interes(self):
        return self._saldo * 0.04 / 12  # 4% anual, dividido en 12 meses


class CuentaInversion(CuentaBancariaAbstracta):
    def __init__(self, titular, saldo=0, riesgo="bajo"):
        super().__init__(titular, saldo)
        self.riesgo = riesgo

    def tipo_cuenta(self):
        return f"Cuenta de Inversión (riesgo {self.riesgo})"

    def calcular_interes(self):
        tasas = {"bajo": 0.06, "medio": 0.09, "alto": 0.14}
        tasa = tasas.get(self.riesgo, 0.06)
        return self._saldo * tasa / 12


# Intentar instanciar la clase abstracta lanza error:
# cuenta = CuentaBancariaAbstracta("Ana")
# → TypeError: Can't instantiate abstract class

ahorro    = CuentaAhorros("Ana García", 10000)
inversion = CuentaInversion("Luis Pérez", 50000, riesgo="medio")

print(ahorro.resumen_completo())
print()
print(inversion.resumen_completo())

Abstracción vs Encapsulamiento — la diferencia

# La confusión más común:

# ENCAPSULAMIENTO → CÓMO proteges los datos
#   Oculta la implementación interna
#   Se logra con modificadores de acceso (private, protected)
#   Pregunta: ¿quién puede acceder a este dato?

# ABSTRACCIÓN → QUÉ expones al exterior
#   Oculta la complejidad del sistema
#   Se logra con interfaces y clases abstractas
#   Pregunta: ¿qué operaciones necesita conocer el usuario?

# Analogía del coche:
#   ABSTRACCIÓN:    el conductor ve volante, acelerador, freno
#   ENCAPSULAMIENTO: el motor está encapsulado, no puedes tocarlo directamente

El proyecto completo — los 4 pilares juntos

from abc import ABC, abstractmethod

# ============================================
# ABSTRACCIÓN: define el contrato
# ============================================
class CuentaBancariaBase(ABC):
    def __init__(self, titular, saldo=0):
        self.titular  = titular
        self.__saldo  = saldo        # ENCAPSULAMIENTO: atributo privado
        self._historial = []

    # ENCAPSULAMIENTO: acceso controlado al saldo
    @property
    def saldo(self):
        return self.__saldo

    def _modificar_saldo(self, cantidad):
        """Método protegido para modificar el saldo internamente."""
        self.__saldo += cantidad
        self._historial.append(f"Movimiento: {cantidad:+.2f} → Saldo: {self.__saldo:.2f}")

    def depositar(self, monto):
        if monto <= 0:
            raise ValueError("El monto debe ser positivo")
        self._modificar_saldo(monto)

    @abstractmethod  # ABSTRACCIÓN: obliga a las subclases a implementarlo
    def retirar(self, monto):
        pass

    @abstractmethod
    def tipo_cuenta(self):
        pass

    def ver_historial(self):
        return "\n".join(self._historial) or "Sin movimientos"

    def __str__(self):
        return f"[{self.tipo_cuenta()}] {self.titular} | ${self.saldo:.2f}"


# ============================================
# HERENCIA: especialización de la clase base
# ============================================
class CuentaAhorros(CuentaBancariaBase):
    def __init__(self, titular, saldo=0, tasa_anual=0.04):
        super().__init__(titular, saldo)
        self.tasa_anual = tasa_anual

    def tipo_cuenta(self):
        return "Ahorros"

    def retirar(self, monto):         # POLIMORFISMO: implementación propia
        if monto > self.saldo:
            raise ValueError("Saldo insuficiente")
        self._modificar_saldo(-monto)

    def aplicar_interes_mensual(self):
        interes = self.saldo * self.tasa_anual / 12
        self.depositar(interes)


class CuentaCorriente(CuentaBancariaBase):
    def __init__(self, titular, saldo=0, sobregiro=500):
        super().__init__(titular, saldo)
        self.sobregiro = sobregiro

    def tipo_cuenta(self):
        return "Corriente"

    def retirar(self, monto):         # POLIMORFISMO: implementación propia
        if monto > self.saldo + self.sobregiro:
            raise ValueError("Supera el límite permitido")
        self._modificar_saldo(-monto)


# ============================================
# POLIMORFISMO: función que trabaja con cualquier cuenta
# ============================================
def generar_reporte(cuentas: list):
    print("=" * 50)
    print("       REPORTE DE CUENTAS")
    print("=" * 50)
    total = 0
    for cuenta in cuentas:        # ← no importa el tipo exacto
        print(cuenta)             # ← polimorfismo: __str__ de cada subclase
        total += cuenta.saldo
    print(f"\nTotal en custodia: ${total:,.2f}")
    print("=" * 50)


# Usando el sistema completo
cuentas = [
    CuentaAhorros("Ana García", 5000, tasa_anual=0.05),
    CuentaCorriente("Luis Pérez", 2000, sobregiro=1000),
    CuentaAhorros("María López", 15000, tasa_anual=0.04),
]

cuentas[0].depositar(500)
cuentas[0].aplicar_interes_mensual()
cuentas[1].retirar(200)

generar_reporte(cuentas)

POO vs Programación Estructurada — ¿cuándo usar cada una?

AspectoProgramación EstructuradaProgramación Orientada a Objetos
OrganizaciónFunciones y procedimientosClases y objetos
DatosVariables separadas de la lógicaDatos y métodos juntos en el objeto
ReutilizaciónFunciones reutilizablesClases reutilizables con herencia
EscalabilidadDifícil en proyectos grandesDiseñada para crecer
Ideal paraScripts, automatización, algoritmosApps, sistemas, backends, juegos
Lenguajes típicosC, Bash, FortranPython, Java, C++, C#, JavaScript

💡 Importante: muchos lenguajes modernos como Python soportan ambos paradigmas. Puedes escribir Python perfectamente sin usar POO. La usas cuando el problema lo justifica: cuando tienes entidades del mundo real que modelar, cuando el proyecto es grande o cuando necesitas que el código sea reutilizable y extensible.


Errores comunes al aprender POO

❌ Crear clases para todo, incluso cuando no hace falta

# ❌ Sobreingeniería innecesaria
class Sumador:
    def sumar(self, a, b):
        return a + b

s = Sumador()
resultado = s.sumar(5, 3)  # ¿para qué crear una clase para esto?

# ✅ Una función simple es suficiente
def sumar(a, b):
    return a + b

❌ Confundir herencia con composición

# Regla: usa herencia cuando el objeto ES del tipo padre
# Usa composición cuando el objeto TIENE algo del otro tipo

# ✅ Herencia: un CocheElectrico ES un Coche
class Coche: pass
class CocheElectrico(Coche): pass

# ✅ Composición: un Coche TIENE un Motor (no ES un Motor)
class Motor: pass
class CocheConMotor:
    def __init__(self):
        self.motor = Motor()  # composición

❌ Ignorar el encapsulamiento y hacerlo todo público

# ❌ Todo público — cualquiera puede romper el estado interno
class Producto:
    def __init__(self):
        self.precio = 0   # modificable desde fuera sin control

# ✅ Encapsulado con validación
class Producto:
    def __init__(self):
        self.__precio = 0

    @property
    def precio(self): return self.__precio

    @precio.setter
    def precio(self, valor):
        if valor < 0: raise ValueError("El precio no puede ser negativo")
        self.__precio = valor

Resumen: los 4 pilares en una tabla

Pilar¿Qué hace?AnalogíaEn Python
🔒 EncapsulamientoProtege los datos internos y controla el accesoEl cajero automático: interfaz controladaself.__atributo, @property
👨‍👩‍👧 HerenciaReutiliza código creando clases especializadasHijo hereda rasgos del padre y añade los propiosclass Hija(Padre):, super()
🎭 PolimorfismoMismo método, comportamiento diferente según el objetoEl botón "play" hace cosas distintas en Spotify o NetflixSobreescritura de métodos
🎨 AbstracciónMuestra lo esencial, oculta la complejidadConducir un coche sin saber cómo funciona el motorABC, @abstractmethod

Resumen: lo que aprendiste hoy

  • ✅ La POO organiza el código en objetos con datos (atributos) y comportamientos (métodos)
  • ✅ Una clase es el molde; un objeto es la instancia concreta creada con ese molde
  • Encapsulamiento — protege los datos con atributos privados y métodos de acceso controlado
  • Herencia — permite crear clases especializadas que reutilizan el código de la clase padre
  • Polimorfismo — el mismo método se comporta diferente según el tipo de objeto
  • Abstracción — define contratos con clases abstractas, oculta la implementación
  • ✅ Los 4 pilares trabajan juntos — ninguno existe de forma completamente aislada
  • ✅ Usa herencia cuando el objeto "ES" del tipo padre; composición cuando "TIENE" algo
  • ✅ No todo necesita POO — para scripts simples la programación estructurada está bien
  • ✅ Python, Java, C++, C# y JavaScript soportan POO — el paradigma más demandado en el mercado

🧪 ¿Tienes claros los conceptos base de POO?

La POO es uno de los temas más evaluados en cualquier lenguaje. Nuestro test de conceptos base te dirá exactamente qué tienes sólido y qué necesitas repasar antes de avanzar a patrones de diseño y arquitectura.

👉 Test: POO Conceptos Base 👉 Test: Python Intermedio 

¿Cuál de los 4 pilares te resultó más difícil de entender: el polimorfismo, la abstracción o la diferencia entre los dos? ¿Ya usabas clases en Python o Java sin conocer estos nombres formales? Cuéntanos en los comentarios 👇 — respondemos todos. 🚀

Etiquetas: test POO POO

¿Te ha gustado esta entrada?

Compártela con tus compañeros para que también sigan aprendiendo.

Comunidad y Comentarios

0 COMENTARIOS

No hay comentarios todavía. Sé el primero en compartir tu opinión.

Escribe tu opinión
Respondiendo a