Origen de los Patrones de Diseño

📅 Actualizado en febrero 2026 ✍️ Ángel López ⏱️ 18 min de lectura ✓ Nivel intermedio ★ ★ ★ ★ ★ (5/5)

🧩 ¿Qué son los patrones de diseño?

Un patrón de diseño (design pattern) es una solución general y reutilizable a un problema que ocurre con frecuencia en el diseño de software orientado a objetos. No es un fragmento de código listo para copiar y pegar, sino una plantilla conceptual que describe cómo resolver un tipo de problema y que puede adaptarse a situaciones concretas.

La idea de los patrones nace de una observación fundamental: los desarrolladores experimentados se enfrentan una y otra vez a problemas similares, y con el tiempo descubren que ciertas estructuras de clases y objetos funcionan mejor que otras. Un patrón de diseño captura esa experiencia colectiva y la formaliza en un formato que puede transmitirse, enseñarse y reutilizarse.

💡
Concepto clave: Un patrón de diseño no es código: es una descripción de cómo resolver un problema. Cada implementación concreta será diferente, pero la estructura subyacente es la misma.

Cada patrón se define mediante cuatro elementos esenciales:

Elemento Descripción
NombreIdentificador que permite referirse al patrón de forma concisa (ej. Singleton, Observer)
ProblemaLa situación recurrente de diseño donde el patrón es aplicable
SoluciónLa estructura de clases, objetos y relaciones que resuelve el problema
ConsecuenciasLos beneficios y compromisos (trade-offs) que implica usar el patrón

🏛️ Origen en la arquitectura: Christopher Alexander

El concepto de «patrón de diseño» no nació en la informática, sino en la arquitectura tradicional. El arquitecto austro-británico-americano Christopher Alexander (Viena, 1936 – 2022) fue el primero en formalizar la idea de que los buenos diseños comparten soluciones recurrentes a problemas comunes.

En 1977, Alexander publicó su obra maestra: A Pattern Language: Towns, Buildings, Construction, un libro de más de 1.100 páginas que catalogaba 253 patrones arquitectónicos, desde la escala de regiones y ciudades hasta detalles como la ubicación de una ventana o el diseño de una escalera. Cada patrón seguía una estructura rigurosa: nombre, problema, contexto, solución y relaciones con otros patrones.

💡
Dato histórico: Christopher Alexander estudió Matemáticas y Arquitectura en Cambridge, obtuvo el primer doctorado en Arquitectura de Harvard y recibió la Medalla de Oro del Instituto Americano de Arquitectos: la primera vez que se otorgó por investigación. Su influencia en la informática fue mayor incluso que en la propia arquitectura.

La filosofía de Alexander se resumía en una idea poderosa: los problemas de diseño se repiten, y las mejores soluciones también. Si identificamos esas soluciones recurrentes y las catalogamos, cualquier persona — no solo un experto — puede crear diseños de calidad combinando patrones probados.

▶️ De la arquitectura al software

En 1987, una década después de A Pattern Language, los ingenieros de software Kent Beck y Ward Cunningham presentaron en la conferencia OOPSLA (Object-Oriented Programming, Systems, Languages & Applications) la idea de aplicar los patrones de Alexander al diseño de software. Su propuesta causó un impacto enorme: los desarrolladores reconocieron de inmediato que los mismos principios de soluciones recurrentes eran aplicables a los problemas del diseño orientado a objetos.

Como dato adicional, fue precisamente Ward Cunningham quien, inspirado por los patrones de Alexander, creó la primera wiki de la historia — la WikiWikiWeb — como repositorio colaborativo para documentar patrones de diseño de software. Esta tecnología sería más tarde la base de Wikipedia.

📖 El Gang of Four y el libro que cambió la ingeniería de software

El paso definitivo de los patrones de diseño de la arquitectura a la ingeniería de software se produjo en 1994, con la publicación de Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley). Sus cuatro autores son conocidos colectivamente como el Gang of Four (GoF):

Autor Afiliación en 1994 Contribución destacada
Erich GammaZúrich, SuizaCo-creador de JUnit y Eclipse
Richard HelmMontreal, CanadáConsultor de arquitectura de software
Ralph JohnsonUniversidad de IllinoisInvestigador en frameworks orientados a objetos
John VlissidesIBM Research, Nueva YorkInvestigador en patrones y herramientas de diseño (1961–2005)

El libro se gestó a partir de un encuentro entre Gamma y Helm en la conferencia OOPSLA de 1990, donde ambos descubrieron su interés compartido por catalogar soluciones recurrentes en el diseño orientado a objetos. Pronto se unieron Johnson y Vlissides. El resultado fue un catálogo de 23 patrones de diseño organizados en tres categorías que se ha vendido en más de 500.000 copias y ha sido traducido a 13 idiomas.

Principios clave del GoF: El libro establece dos reglas fundamentales que todo programador orientado a objetos debería conocer: (1) «Programa hacia una interfaz, no hacia una implementación» y (2) «Favorece la composición de objetos sobre la herencia de clases».

Más de tres décadas después de su publicación, el libro sigue siendo un referente. Los 23 patrones GoF forman la base sobre la que se construyen frameworks modernos como Spring, Jakarta EE, Hibernate, Angular y React. Comprender estos patrones no es solo una cuestión académica: es la clave para entender cómo funcionan las herramientas que usamos a diario en la industria del software.

⚡ ¿Por qué importan los patrones de diseño?

Los patrones de diseño no son simples curiosidades teóricas. Su adopción generalizada responde a beneficios concretos y medibles en el desarrollo profesional de software:

Vocabulario compartido: Cuando un equipo habla de «aplicar un Observer» o «usar un Factory Method», todos entienden inmediatamente la estructura propuesta. Esto reduce errores de comunicación y acelera las revisiones de código.

Soluciones probadas: Cada patrón ha sido validado durante décadas en proyectos reales. Usarlos evita reinventar la rueda y reduce el riesgo de introducir defectos de diseño.

Flexibilidad y mantenibilidad: Los patrones promueven el bajo acoplamiento y la alta cohesión, lo que hace que el código sea más fácil de modificar, extender y probar.

Comprensión de frameworks: Spring usa Factory, Singleton, Proxy y Template Method internamente. Hibernate aplica Proxy, Iterator y Unit of Work. Sin conocer los patrones, es difícil entender por qué estos frameworks funcionan como funcionan.

Riesgo de abuso: El uso indiscriminado de patrones donde no son necesarios conduce al over-engineering: código innecesariamente complejo que resulta más difícil de mantener que una solución directa.

📋 Clasificación de los 23 patrones GoF

El Gang of Four organizó los 23 patrones en tres familias según su propósito. Esta clasificación es universalmente aceptada y se utiliza en todos los libros, cursos y frameworks del mundo:

Categoría Propósito Nº de patrones
🔨 CreacionalesControlan cómo se crean los objetos5
🏗️ EstructuralesDefinen cómo se componen clases y objetos7
🔄 De comportamientoGestionan la comunicación entre objetos11

🔨 Patrones creacionales

Los patrones creacionales abstraen el proceso de instanciación de objetos. En lugar de crear objetos directamente con new, estos patrones proporcionan mecanismos que hacen el sistema independiente de cómo se crean, componen y representan sus objetos.

Patrón Problema que resuelve Ejemplo real
SingletonGarantizar que una clase tenga una sola instanciaConexión a base de datos, logger
Factory MethodDelegar la creación de objetos a subclasesCrear documentos PDF, Word o HTML según tipo
Abstract FactoryCrear familias de objetos relacionados sin especificar clases concretasKits de interfaz gráfica multiplataforma
BuilderConstruir objetos complejos paso a pasoConstruir un pedido con productos, descuentos y envío
PrototypeCrear objetos copiando un prototipo existenteClonar configuraciones en un editor gráfico
Java — Factory Method (ejemplo simplificado)
// Interfaz del producto
interface Notificacion {
    void enviar(String mensaje);
}

// Productos concretos
class NotificacionEmail implements Notificacion {
    public void enviar(String mensaje) {
        System.out.println("Email: " + mensaje);
    }
}

class NotificacionSMS implements Notificacion {
    public void enviar(String mensaje) {
        System.out.println("SMS: " + mensaje);
    }
}

// Factory Method: la subclase decide qué objeto crear
abstract class NotificacionFactory {
    public abstract Notificacion crearNotificacion();

    public void notificar(String mensaje) {
        Notificacion n = crearNotificacion();  // Factory Method
        n.enviar(mensaje);
    }
}

class EmailFactory extends NotificacionFactory {
    public Notificacion crearNotificacion() {
        return new NotificacionEmail();
    }
}

class SMSFactory extends NotificacionFactory {
    public Notificacion crearNotificacion() {
        return new NotificacionSMS();
    }
}

🏗️ Patrones estructurales

Los patrones estructurales se ocupan de cómo se ensamblan clases y objetos para formar estructuras más grandes y complejas. Facilitan el diseño identificando formas sencillas de establecer relaciones entre entidades.

Patrón Problema que resuelve Ejemplo real
AdapterHacer compatible una interfaz con otraAdaptar una API antigua a un sistema nuevo
BridgeSeparar la abstracción de su implementaciónFormas geométricas con distintos motores de renderizado
CompositeTratar objetos individuales y compuestos de forma uniformeMenú con submenús anidados, sistema de archivos
DecoratorAñadir funcionalidad a un objeto dinámicamenteStreams de Java: BufferedReader(new FileReader(...))
FacadeSimplificar el acceso a un subsistema complejoAPI unificada para un sistema de pagos
FlyweightCompartir estado para soportar gran cantidad de objetosCaracteres en un editor de texto (mismo glifo, distinta posición)
ProxyProporcionar un sustituto o representante de otro objetoCarga perezosa de imágenes, proxy de seguridad
💡
Conexión con el artículo original: Los patrones «Continente-Contenido» y «Objeto-Componente» que se mencionaban en la versión anterior de este artículo son variantes del patrón Composite del GoF: un objeto que contiene otros objetos y los trata de forma uniforme.
Java — Decorator (ejemplo simplificado)
// Componente base
interface Cafe {
    String descripcion();
    double precio();
}

// Componente concreto
class CafeSimple implements Cafe {
    public String descripcion() { return "Café solo"; }
    public double precio() { return 1.50; }
}

// Decorator base
abstract class CafeDecorator implements Cafe {
    protected Cafe cafeDecorado;

    public CafeDecorator(Cafe cafe) {
        this.cafeDecorado = cafe;
    }
}

// Decoradores concretos
class ConLeche extends CafeDecorator {
    public ConLeche(Cafe cafe) { super(cafe); }
    public String descripcion() { return cafeDecorado.descripcion() + " + leche"; }
    public double precio() { return cafeDecorado.precio() + 0.30; }
}

class ConCanela extends CafeDecorator {
    public ConCanela(Cafe cafe) { super(cafe); }
    public String descripcion() { return cafeDecorado.descripcion() + " + canela"; }
    public double precio() { return cafeDecorado.precio() + 0.15; }
}

// Uso: se "decoran" objetos dinámicamente
// Cafe pedido = new ConCanela(new ConLeche(new CafeSimple()));
// pedido.descripcion() → "Café solo + leche + canela"
// pedido.precio()      → 1.95

🔄 Patrones de comportamiento

Los patrones de comportamiento se centran en los algoritmos y la asignación de responsabilidades entre objetos. Describen no solo los patrones de objetos o clases, sino también los patrones de comunicación entre ellos.

Patrón Problema que resuelve Ejemplo real
ObserverNotificar cambios de estado a múltiples objetosListeners de eventos, suscripciones
StrategyIntercambiar algoritmos en tiempo de ejecuciónDistintas estrategias de ordenación
CommandEncapsular una petición como un objetoDeshacer/rehacer en un editor
IteratorRecorrer una colección sin exponer su estructura internafor (Item i : coleccion) en Java
Template MethodDefinir el esqueleto de un algoritmo, delegando pasos a subclasesProceso de autenticación con pasos personalizables
StateCambiar el comportamiento de un objeto según su estadoMáquina expendedora, estados de un pedido
MediatorReducir dependencias directas entre objetosTorre de control de un aeropuerto
Chain of ResponsibilityPasar una petición a lo largo de una cadena de manejadoresFiltros de servlet, middleware
VisitorAñadir operaciones a una estructura sin modificar sus clasesCompilador que recorre un AST
MementoCapturar y restaurar el estado interno de un objetoGuardar partida en un videojuego
InterpreterDefinir una gramática y un intérprete para un lenguajeEvaluador de expresiones regulares
Java — Observer (ejemplo simplificado)
import java.util.ArrayList;
import java.util.List;

// Sujeto observable
class TiendaOnline {
    private List<ClienteObserver> suscriptores = new ArrayList<>();
    private String productoDisponible;

    public void suscribir(ClienteObserver cliente) {
        suscriptores.add(cliente);
    }

    public void notificarDisponibilidad(String producto) {
        this.productoDisponible = producto;
        for (ClienteObserver c : suscriptores) {
            c.actualizar(producto);
        }
    }
}

// Observer
interface ClienteObserver {
    void actualizar(String producto);
}

// Observer concreto
class ClienteEmail implements ClienteObserver {
    private String nombre;

    public ClienteEmail(String nombre) { this.nombre = nombre; }

    public void actualizar(String producto) {
        System.out.println("Notificando a " + nombre + ": ¡" + producto + " disponible!");
    }
}

// Uso:
// TiendaOnline tienda = new TiendaOnline();
// tienda.suscribir(new ClienteEmail("Ana"));
// tienda.suscribir(new ClienteEmail("Carlos"));
// tienda.notificarDisponibilidad("Java 21 Certification Guide");
// → Notificando a Ana: ¡Java 21 Certification Guide disponible!
// → Notificando a Carlos: ¡Java 21 Certification Guide disponible!

💻 Ejemplo integrador en Java: patrón Singleton

El patrón Singleton garantiza que una clase tenga exactamente una sola instancia y proporciona un punto de acceso global a ella. Es uno de los patrones más sencillos de entender, pero también uno de los más polémicos por sus implicaciones en el testing y la concurrencia.

🔹 Problema

Necesitamos que ciertos recursos compartan una única instancia a lo largo de toda la aplicación: una conexión a base de datos, un registro de log, un gestor de configuración. Crear múltiples instancias desperdiciaría recursos o provocaría inconsistencias.

🔹 Solución en Java (thread-safe con Bill Pugh idiom)

Java — Singleton (Bill Pugh idiom)
public class ConfiguracionApp {

    // Constructor privado: nadie puede instanciar desde fuera
    private ConfiguracionApp() {
        System.out.println("Cargando configuración...");
    }

    // Clase interna estática: se carga solo cuando se necesita (lazy)
    private static class Holder {
        private static final ConfiguracionApp INSTANCIA = new ConfiguracionApp();
    }

    // Punto de acceso global
    public static ConfiguracionApp getInstancia() {
        return Holder.INSTANCIA;
    }

    // Métodos de negocio
    private String idioma = "es";
    private int maxConexiones = 10;

    public String getIdioma() { return idioma; }
    public void setIdioma(String idioma) { this.idioma = idioma; }
    public int getMaxConexiones() { return maxConexiones; }
}
Java — Uso del Singleton
public class Main {
    public static void main(String[] args) {
        // Obtener la instancia única
        ConfiguracionApp config1 = ConfiguracionApp.getInstancia();
        ConfiguracionApp config2 = ConfiguracionApp.getInstancia();

        // Ambas referencias apuntan al mismo objeto
        System.out.println(config1 == config2);  // true

        // Modificar desde cualquier referencia afecta a todas
        config1.setIdioma("en");
        System.out.println(config2.getIdioma()); // "en"
    }
}

// Salida:
// Cargando configuración...
// true
// en
⚠️
Precaución: El Singleton dificulta las pruebas unitarias porque introduce estado global. En aplicaciones modernas, se recomienda usar inyección de dependencias (como la que ofrece Spring con @Scope("singleton")) en lugar de implementar Singleton manualmente. El patrón sigue siendo útil para entender el concepto, pero su implementación directa se considera un antipatrón en ciertos contextos.

🎯 Cuándo usar y cuándo no usar patrones

La experiencia del Gang of Four y de décadas de práctica profesional ha condensado reglas claras sobre el uso adecuado de los patrones de diseño:

✅ Usa un patrón cuando...

El problema que enfrentas coincide con el que describe el patrón. Tu diseño necesita flexibilidad ante cambios futuros que puedes anticipar razonablemente. Varios miembros del equipo necesitan entender la arquitectura rápidamente. Estás trabajando en un proyecto con un ciclo de vida largo, donde la mantenibilidad es prioritaria.

❌ Evita un patrón cuando...

La solución directa es más sencilla y no hay indicios de que el diseño necesite evolucionar. Estás añadiendo capas de abstracción «por si acaso». El patrón complica el código más de lo que lo simplifica. El equipo no conoce el patrón y no hay tiempo para formación: un patrón mal implementado es peor que ningún patrón.

Regla de oro: «No busques problemas para tus patrones; busca patrones para tus problemas.» La mejor forma de aprender patrones es reconocerlos en código existente (frameworks, bibliotecas) antes de intentar aplicarlos en proyectos propios.

📝 Ejercicios prácticos

Ejercicio 1: Identifica el patrón

Observa el siguiente fragmento de código Java e identifica qué patrón de diseño GoF se está utilizando. Justifica tu respuesta indicando qué problema resuelve.

Java
interface Descuento {
    double aplicar(double precio);
}

class DescuentoNavidad implements Descuento {
    public double aplicar(double precio) { return precio * 0.80; }
}

class DescuentoBlackFriday implements Descuento {
    public double aplicar(double precio) { return precio * 0.50; }
}

class CarritoCompra {
    private Descuento estrategia;
    public void setDescuento(Descuento d) { this.estrategia = d; }
    public double calcularPrecio(double precio) { return estrategia.aplicar(precio); }
}

Ejercicio 2: Implementa un Factory Method

Crea un sistema de transporte con una interfaz Vehiculo que tenga un método entregar(). Implementa Camion y Barco como vehículos concretos. Luego crea una clase abstracta LogisticaFactory con un factory method crearVehiculo() y dos subclases: LogisticaTerrestre (que crea camiones) y LogisticaMaritima (que crea barcos).

Ejercicio 3: Clasifica los patrones

Coloca cada uno de los siguientes patrones en su categoría correcta (Creacional, Estructural o Comportamiento): Observer, Builder, Adapter, Singleton, Strategy, Composite, Factory Method, Decorator, Command, Proxy.

Ejercicio 4: Implementa un Decorator

Diseña un sistema de pedidos de pizza. Crea una interfaz Pizza con métodos descripcion() y precio(). Implementa PizzaBasica (Margarita, 8.00€). Luego crea decoradores para ingredientes extra: ConQueso (+1.50€), ConJamon (+2.00€) y ConChampiñones (+1.20€). Muestra cómo pedir una pizza con queso y champiñones.

❓ Preguntas frecuentes sobre Origen de los Patrones de Diseño

Las dudas más comunes respondidas de forma clara y directa.

Son 23 soluciones reutilizables a problemas recurrentes en el diseño de software orientado a objetos, documentados por Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides en su libro de 1994 "Design Patterns: Elements of Reusable Object-Oriented Software". Se clasifican en tres categorías: creacionales, estructurales y de comportamiento.
Los patrones creacionales controlan cómo se crean los objetos (ej. Singleton, Factory). Los estructurales definen cómo se componen clases y objetos para formar estructuras mayores (ej. Adapter, Decorator). Los de comportamiento gestionan la comunicación y responsabilidad entre objetos (ej. Observer, Strategy).
El concepto de patrón aplicado al diseño fue introducido por el arquitecto Christopher Alexander en su libro "A Pattern Language" (1977). En 1987, Kent Beck y Ward Cunningham adaptaron la idea al software, y en 1994 el Gang of Four la formalizó para la ingeniería de software orientada a objetos.
No. Los patrones GoF son independientes del lenguaje de programación. Se pueden implementar en Java, Python, C++, C#, TypeScript o cualquier lenguaje orientado a objetos. El libro original usa ejemplos en C++ y Smalltalk, pero Java es uno de los lenguajes donde más se aplican por su fuerte orientación a objetos.
No. Los patrones deben aplicarse cuando resuelven un problema real de diseño, no de forma indiscriminada. Usar un patrón donde no es necesario añade complejidad innecesaria (over-engineering). La clave es reconocer el problema y elegir el patrón adecuado solo cuando aporta beneficio concreto.
Los más frecuentes en proyectos Java empresariales son: Singleton (instancia única), Factory Method (creación flexible), Observer (notificaciones), Strategy (algoritmos intercambiables), Builder (construcción compleja), Decorator (extensión dinámica) e Iterator (recorrido de colecciones). Muchos frameworks como Spring o Hibernate los usan internamente.
Sí. Aunque fue publicado en 1994, los 23 patrones GoF siguen siendo la base de la arquitectura de software moderna. Frameworks actuales como Spring, Jakarta EE, React y Angular implementan estos patrones internamente. Comprender los patrones GoF es esencial para entender cómo funcionan los frameworks que usamos a diario.
Valora este artículo

💬 Foro de discusión

¿Tienes dudas sobre Origen de los Patrones de Diseño? Comparte tu pregunta con la comunidad.

¿Tienes cuenta? o comenta como invitado ↓

Todavía no hay mensajes. ¡Sé el primero en participar!