$ devBelts
Última revisión: 2026-05-19

Paradigmas: programación funcional vs orientada a objetos

Entiende los dos paradigmas dominantes de programación, sus fortalezas, debilidades y cuándo usar cada uno.

No hay una forma “correcta” de programar. Hay paradigmas — marcos de pensamiento que determinan cómo estructuras tu código, cómo manejas datos y cómo resuelves problemas. Los dos dominantes son la programación orientada a objetos (OOP) y la programación funcional (FP).

En esta guía vas a entender ambos paradigmas, ver ejemplos comparativos y aprender cuándo cada uno brilla.


Programación Orientada a Objetos (OOP)

La OOP organiza el código alrededor de objetos que combinan datos (estado) y comportamiento (métodos).

Los cuatro pilares

// 1. Encapsulamiento: estado privado, interfaz pública
class CuentaBancaria {
    private saldo: number;

    constructor(saldoInicial: number) {
        this.saldo = saldoInicial;
    }

    depositar(monto: number): void {
        if (monto <= 0) throw new Error("Monto inválido");
        this.saldo += monto;
    }

    retirar(monto: number): void {
        if (monto > this.saldo) throw new Error("Fondos insuficientes");
        this.saldo -= monto;
    }

    getSaldo(): number {
        return this.saldo;
    }
}

// 2. Herencia: reutilizar comportamiento
class CuentaAhorro extends CuentaBancaria {
    private tasaInteres: number;

    constructor(saldo: number, tasa: number) {
        super(saldo);
        this.tasaInteres = tasa;
    }

    aplicarInteres(): void {
        const interes = this.getSaldo() * this.tasaInteres;
        this.depositar(interes);
    }
}

// 3. Polimorfismo: misma interfaz, diferente implementación
interface Notificador {
    enviar(mensaje: string): void;
}

class EmailNotificador implements Notificador {
    enviar(mensaje: string) { console.log(`Email: ${mensaje}`); }
}

class SMSNotificador implements Notificador {
    enviar(mensaje: string) { console.log(`SMS: ${mensaje}`); }
}

function notificar(notif: Notificador, msg: string) {
    notif.enviar(msg);  // Funciona con cualquier Notificador
}

// 4. Abstracción: ocultar complejidad
abstract class Repository<T> {
    abstract findById(id: number): T | undefined;
    abstract save(entity: T): void;
    abstract delete(id: number): void;
}

Cuándo brilla OOP

  • Dominios complejos con estado: sistemas bancarios, juegos, simulaciones
  • Frameworks y librerías: React (componentes como clases), Angular
  • Equipos grandes: interfaces claras, encapsulamiento protege el estado
  • Modelado del mundo real: objetos que representan entidades del dominio

Programación Funcional (FP)

La FP organiza el código alrededor de funciones puras que transforman datos inmutables.

Los principios

// 1. Funciones puras: mismo input → mismo output, sin efectos secundarios
function sumar(a: number, b: number): number {
    return a + b;  // Siempre 5 para (2, 3). No modifica nada externo.
}

// ❌ Impura: depende de estado externo
let contador = 0;
function incrementar() {
    contador++;  // Efecto secundario
    return contador;
}

// 2. Inmutabilidad: los datos no se modifican, se transforman
interface Usuario {
    nombre: string;
    activo: boolean;
}

// ❌ Mutación
function desactivar(usuario: Usuario) {
    usuario.activo = false;  // Modifica el original
    return usuario;
}

// ✅ Inmutable
function desactivar(usuario: Usuario): Usuario {
    return { ...usuario, activo: false };  // Nuevo objeto
}

// 3. Funciones de orden superior: funciones que reciben/devuelven funciones
function multiplicarPor(factor: number): (n: number) => number {
    return (n: number) => n * factor;
}

const duplicar = multiplicarPor(2);
const triplicar = multiplicarPor(3);

console.log(duplicar(5));  // 10
console.log(triplicar(5)); // 15

// 4. Composición: combinar funciones simples en complejas
const pipe = <A, B, C>(f: (a: A) => B, g: (b: B) => C) => (x: A): C => g(f(x));

const toUpperCase = (s: string) => s.toUpperCase();
const exclaim = (s: string) => s + "!";
const shout = pipe(toUpperCase, exclaim);

console.log(shout("hola"));  // "HOLA!"

// 5. Manejo de errores con tipos, no excepciones
type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };

function dividir(a: number, b: number): Result<number> {
    if (b === 0) return { ok: false, error: new Error("División por cero") };
    return { ok: true, value: a / b };
}

const resultado = dividir(10, 2);
if (resultado.ok) {
    console.log(resultado.value);  // 5
}

Cuándo brilla FP

  • Transformación de datos: pipelines de ETL, procesamiento de listas
  • Concurrencia: sin estado mutable, no hay race conditions
  • Testing: funciones puras son trivialmente testeables
  • React: componentes funcionales + hooks son el estándar moderno
  • TypeScript: el sistema de tipos se integra naturalmente con FP

Comparación directa

Mismo problema, dos enfoques

Problema: calcular el total de un carrito de compras con descuento e impuesto.

// OOP: comportamiento encapsulado en objetos
class Carrito {
    private items: Item[] = [];

    agregar(item: Item) { this.items.push(item); }

    subtotal(): number {
        return this.items.reduce((sum, item) => sum + item.precio * item.cantidad, 0);
    }

    total(descuento: number, impuesto: number): number {
        const sub = this.subtotal();
        const conDescuento = sub * (1 - descuento);
        return conDescuento * (1 + impuesto);
    }
}

// FP: pipeline de transformaciones
interface Item { precio: number; cantidad: number; }

const subtotal = (items: Item[]): number =>
    items.reduce((sum, item) => sum + item.precio * item.cantidad, 0);

const aplicarDescuento = (descuento: number) => (total: number) =>
    total * (1 - descuento);

const aplicarImpuesto = (impuesto: number) => (total: number) =>
    total * (1 + impuesto);

const calcularTotal = (items: Item[], descuento: number, impuesto: number): number =>
    pipe(
        () => subtotal(items),
        aplicarDescuento(descuento),
        aplicarImpuesto(impuesto),
    )(undefined as never);

El enfoque práctico: mezclar ambos

En la práctica, el mejor código usa ambos paradigmas:

// FP para lógica de negocio (funciones puras, inmutables)
function calcularPrecio(items: Item[], descuento: number): number {
    // ... lógica pura
}

// OOP para infraestructura (encapsulamiento de estado externo)
class DatabaseConnection {
    private pool: Pool;
    connect() { /* ... */ }
    query(sql: string) { /* ... */ }
    close() { /* ... */ }
}

Por qué importa

Entender ambos paradigmas te permite:

  • Elegir la herramienta correcta para cada problema
  • Leer código de otros que usa un paradigma diferente al tuyo
  • Combinar lo mejor de ambos en vez de ser dogmático
  • Entender frameworks: React es funcional, Angular es orientado a objetos

La IA y los paradigmas

Lo bueno

  • Convertir entre paradigmas: la IA puede refactorizar código OOP a FP y viceversa.
  • Explicar patrones: la IA explica Factory, Strategy, Monad con ejemplos.
  • Sugerir el paradigma correcto: describe tu problema y la IA recomienda el enfoque.

Lo que no debes hacer

  • No mezcles paradigmas sin criterio. Tener clases mutables Y funciones puras en el mismo módulo sin organización es caos.
  • No uses FP dogmáticamente. A veces una clase con estado es la solución más simple.
  • No uses OOP dogmáticamente. No todo necesita una jerarquía de herencia.

Desafío: refactoriza entre paradigmas

Objetivo: convertir código entre OOP y FP.

Tu tarea:

  1. Toma esta clase OOP y conviértela a FP:
class ProcesadorTexto {
    private texto: string;
    constructor(texto: string) { this.texto = texto; }
    toUpperCase() { this.texto = this.texto.toUpperCase(); }
    removeVowels() { this.texto = this.texto.replace(/[aeiou]/gi, ''); }
    wordCount() { return this.texto.split(/\s+/).filter(Boolean).length; }
}
  1. Toma estas funciones FP y organízalas en clases OOP:
function crearUsuario(nombre: string, email: string) { /* ... */ }
function validarUsuario(usuario: Usuario) { /* ... */ }
function guardarUsuario(usuario: Usuario) { /* ... */ }
function enviarEmailBienvenida(usuario: Usuario) { /* ... */ }

Bonus: implementa el mismo problema (sistema de inventario) usando ambos paradigmas y compara.


Para seguir explorando


Resumen

  • OOP organiza código en objetos que combinan estado y comportamiento (encapsulamiento, herencia, polimorfismo, abstracción).
  • FP organiza código en funciones puras que transforman datos inmutables (pureza, inmutabilidad, composición, funciones de orden superior).
  • OOP brilla en dominios con estado complejo, frameworks y modelado del mundo real.
  • FP brilla en transformación de datos, concurrencia, testing y React.
  • El enfoque práctico mezcla ambos: FP para lógica de negocio, OOP para infraestructura.
  • El dogmatismo de paradigma es contraproducente — usa lo que resuelva el problema.

En la próxima guía vamos a construir interfaces modernas: React desde cero: componentes, props y estado — la librería de UI más popular del mundo.