Guía Completa de 23 Patrones de Diseño

Guía Completa de 23 Patrones de Diseño

Resumen General de Patrones de Diseño

Categoría Nombre del Patrón Nombre en Inglés Propósito Principal
Creacionales Singleton Singleton Garantizar una única instancia de una clase
Creacionales Método Fábrica Factory Method Crear objetos, permitiendo a las subclases decidir qué instanciar
Creacionales Fábrica Abstracta Abstract Factory Crear familias de objetos relacionados sin especificar clases concretas
Creacionales Constructor Builder Separar la construcción de un objeto complejo de su representación
Creacionales Prototipo Prototype Crear objetos mediante clonación
Estructurales Adaptador Adapter Convertir interfaces para hacer clases incompatibles compatibles
Estructurales Puente Bridge Separar la abstracción de su implementación para que puedan variar independientemente
Estructurales Compuesto Composite Tratar estructuras en árbol de forma uniforme, combinando hojas y componentes
Estructurales Decorador Decorator Agregar funcionalidad dinámicamente sin modificar la clase original
Estructurales Fachada Facade Proporcionar una interfaz unificada a un subsistema complejo
Estructurales Peso Mosca Flyweight Compartir objetos de grano fino para reducir el uso de memoria
Estructurales Proxy Proxy Controlar el acceso a un objeto
Comportamentales Cadena de Responsabilidad Chain of Responsibility Pasar peticiones a lo largo de una cadena de objetos hasta que sea manejada
Comportamentales Comando Command Encapsular una petición como objeto
Comportamentales Intérprete Interpreter Definir la gramática de un lenguaje y su interpretación
Comportamentales Iterador Iterator Proporcionar un acceso secuencial a los elementos de una colección
Comportamentales Mediador Mediator Centralizar la comunicación entre objetos sin que se referencien directamente
Comportamentales Memento Memento Capturar y almacenar el estado interno de un objeto para poder restaurarlo
Comportamentales Observador Observer Notificar a objetos dependientes cuando un objeto cambie de estado
Comportamentales Estado State Permitir que un objeto cambie su comportamiento cuando cambie su estado interno
Comportamentales Estrategia Strategy Definir una familia de algoritmos y encapsularlos para que sean intercambiables
Comportamentales Método Plantilla Template Method Definir la estructura de un algoritmo, delegando los pasos a las subclases
Comportamentales Visitante Visitor Definir nuevas operaciones sobre objetos sin modificar sus clases

️ Patrones Creacionales (5)

1. Singleton (Singleton)

// Asegura que una clase tenga solo una instancia
public class GestorConfiguracion {
    private static GestorConfiguracion instanciaUnica;
    private String configuracion;
    
    private GestorConfiguracion() {
        this.configuracion = "configuracion por defecto";
    }
    
    public static GestorConfiguracion obtenerInstancia() {
        if (instanciaUnica == null) {
            instanciaUnica = new GestorConfiguracion();
        }
        return instanciaUnica;
    }
    
    public String getConfiguracion() {
        return configuracion;
    }
}

Casos de uso: Gestores de configuración, pools de hilos, cachés, objetos de registro

2. Método Fábrica (Factory Method)

// Permite a las subclases decidir qué clase instanciar
public interface Producto {
    void operar();
}

public abstract class Creador {
    public abstract Producto crearProducto();
    
    public void procesarProducto() {
        Producto producto = crearProducto();
        producto.operar();
    }
}

Casos de uso: Diseño de frameworks, sistemas de plugins, creación de componentes de interfaz

3. Fábrica Abstracta (Abstract Factory)

// Crea familias de objetos relacionados sin especificar clases concretas
public interface FabricaUI {
    Boton crearBoton();
    Menu crearMenu();
}

public class FabricaWindows implements FabricaUI {
    public Boton crearBoton() {
        return new BotonWindows();
    }
    
    public Menu crearMenu() {
        return new MenuWindows();
    }
}

Casos de uso: Componentes de UI multiplataforma, capas de acceso a datos, temas de juegos

4. Builder (Constructor)

// Construcción paso a paso de objetos complejos
public class Ordenador {
    private String procesador;
    private int memoriaRAM;
    private String tarjetaGrafica;
    
    public static class ConstructorOrdenador {
        private Ordenador ordenador = new Ordenador();
        
        public ConstructorOrdenador conProcesador(String procesador) {
            ordenador.procesador = procesador;
            return this;
        }
        
        public ConstructorOrdenador conMemoriaRAM(int memoria) {
            ordenador.memoriaRAM = memoria;
            return this;
        }
        
        public Ordenador construir() {
            return ordenador;
        }
    }
}

Casos de uso: Construcción de objetos complejos, lamadas encadenadas, parámetros opcionales

5. Prototipo (Prototype)

// Creación de objetos mediante clonación
public class Documento implements Cloneable {
    private String contenido;
    private String formato;
    
    public Documento(String contenido, String formato) {
        this.contenido = contenido;
        this.formato = formato;
    }
    
    @Override
    public Documento clonar() {
        try {
            return (Documento) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException("Error al clonar el documento");
        }
    }
}

Casos de uso: Creación de objetos costosa, necesidad de objetos similares, guardado de estado de objetos

️ Patrones Estructurales (7)

6. Adaptador (Adapter)

// Convierte la interfaz de una clase a otra interfaz esperada
public class AdaptadorTarjeta implements LectorTarjetas {
    private MaquinaExtraccion maquina;
    
    public AdaptadorTarjeta(MaquinaExtraccion maquina) {
        this.maquina = maquina;
    }
    
    public void leerTarjeta(Tarjeta tarjeta) {
        maquina.procesarExtraccion(tarjeta.getNumero(), tarjeta.getPin());
    }
}

Casos de uso: Compatibilidad entre sistemas antiguos y nuevos, integración de librerías de terceros, conversión de interfaces

7. Puente (Bridge)

// Separa la abstracción de su implementación para que puedan variar independientemente
public abstract class Figura {
    protected ColorImplementacion color;
    
    public Figura(ColorImplementacion color) {
        this.color = color;
    }
    
    public abstract void dibujar();
}

public class Circulo extends Figura {
    private int radio;
    
    public Circulo(ColorImplementacion color, int radio) {
        super(color);
        this.radio = radio;
    }
    
    public void dibujar() {
        color.aplicarColor();
        System.out.println("Dibujando círculo con radio: " + radio);
    }
}

Casos de uso: Aplicaciones multiplataforma, sistemas con múltiples dimensiones de cambio, drivers de base de datos

8. Compuesto (Composite)

// Trata objetos compuestos y objetos individuos de manera uniforme
public abstract class Componente {
    protected String nombre;
    
    public void agregar(Componente componente) {
        throw new UnsupportedOperationException();
    }
    
    public void eliminar(Componente componente) {
        throw new UnsupportedOperationException();
    }
    
    public abstract void mostrar();
}

public class Hoja extends Componente {
    public Hoja(String nombre) {
        this.nombre = nombre;
    }
    
    public void mostrar() {
        System.out.println("Hoja: " + nombre);
    }
}

Casos de uso: Sistemas de archivos, árboles de componentes UI, estructuras organizacionales

9. Decorador (Decorator)

// Agrega responsabilidades adicionales a un objeto dinámicamente
public abstract class CafeDecorador implements Cafe {
    protected Cafe cafeDecorado;
    
    public CafeDecorador(Cafe cafe) {
        this.cafeDecorado = cafe;
    }
    
    public double obtenerPrecio() {
        return cafeDecorado.obtenerPrecio();
    }
    
    public String getDescripcion() {
        return cafeDecorado.getDescripcion();
    }
}

Casos de uso: Procesamiento de streams, verificación de permisos, registro de actividades, caché

10. Fachada (Facade)

// Proporciona una interfaz unificada a un conjunto de interfaces en un subsistema
public class SistemaMultimedia {
    private Amplificador amplificador;
    private Proyector proyector;
    private ReproductorDVD reproductor;
    
    public void iniciarPelicula(String pelicula) {
        amplificador.encender();
        proyector.encender();
        reproductor.cargarPelicula(pelicula);
        reproductor.reproducir();
    }
}

Casos de uso: Simplificar sistemas complejos, desacoplar capas, encapsular APIs

11. Peso Mosca (Flyweight)

// Comparte objetos de grano fino para reducir el uso de memoria
public class FabricaPesoMosca {
    private Map<String, PesoMosca> cache = new HashMap<>();
    
    public PesoMosca obtenerPesoMosca(String clave) {
        if (!cache.containsKey(clave)) {
            cache.put(clave, new PesoMoscaConcreta(clave));
        }
        return cache.get(clave);
    }
}

public class PesoMoscaConcreta implements PesoMosca {
    private String estadoIntrinseco;
    
    public PesoMoscaConcreca(String estado) {
        this.estadoIntrinseco = estado;
    }
    
    public void operar(String estadoExtrinseco) {
        System.out.println("Estado intrínseco: " + estadoIntrinseco + ", Estado extrínseco: " + estadoExtrinseco);
    }
}

Casos de uso: Gran cantidad de objetos similares, renderizado de caracteres/gráficos, pools de conexiones a base de datos

12. Proxy (Proxy)

// Proporciona un sustituto o placeholder para otro objeto
public class ProxyImagen implements Imagen {
    private ImagenReal imagenReal;
    private String rutaArchivo;
    
    public ProxyImagen(String ruta) {
        this.rutaArchivo = ruta;
    }
    
    public void mostrar() {
        if (imagenReal == null) {
            imagenReal = new ImagenReal(rutaArchivo);
        }
        imagenReal.mostrar();
    }
}

Casos de uso: Proxy remoto, proxy virtual, proxy de protección, proxy de caché

️ Patrones Comportamentales (11)

13. Cadena de Responsabilidad (Chain of Responsibility)

// Pasa la petición a lo largo de una cadena de objetos hasta que es manejada
public abstract class Manejador {
    protected Manejador siguienteManejador;
    
    public void establecerSiguiente(Manejador manejador) {
        this.siguienteManejador = manejador;
    }
    
    public abstract void procesar(Peticion peticion);
}

public class ManejadorConcreto extends Manejador {
    public void procesar(Peticion peticion) {
        if (puedeProcesar(peticion)) {
            // Procesar la petición
        } else if (siguienteManejador != null) {
            siguienteManejador.procesar(peticion);
        }
    }
    
    private boolean puedeProcesar(Peticion peticion) {
        // Lógica para determinar si puede procesar
        return true;
    }
}

Casos de uso: Flujos de aprobación, manejo de excepciones, cadenas de filtros

14. Comando (Command)

// Encapsula una petición como objeto
public interface Orden {
    void ejecutar();
    void deshacer();
}

public class OrdenPrender implements Orden {
    private Dispositivo dispositivo;
    
    public OrdenPrender(Dispositivo dispositivo) {
        this.dispositivo = dispositivo;
    }
    
    public void ejecutar() {
        dispositivo.encender();
    }
    
    public void deshacer() {
        dispositivo.apagar();
    }
}

Casos de uso: Botones de interfaz gráfica, gestión de transacciones, macros, tareas en cola

15. Intérprete (Interpreter)

// Define la representación gramatical de un lenguaje y su interpretación
public interface Expresion {
    int interpretar(Contexto contexto);
}

public class ExpresionNumerica implements Expresion {
    private int numero;
    
    public ExpresionNumerica(int numero) {
        this.numero = numero;
    }
    
    public int interpretar(Contexto contexto) {
        return numero;
    }
}

Casos de uso: Análisis de SQL, expresiones regulares, compiladores

16. Iterador (Iterator)

// Proporciona un medio para acceder secuencialmente a los elementos de una colección
public interface Iterador<T> {
    boolean tieneSiguiente();
    T siguiente();
    void remover();
}

public class ColeccionIterable<T> implements Iterable<T> {
    private List<T> elementos = new ArrayList<>();
    
    public Iterador<T> crearIterador() {
        return new IteradorInterno<>(elementos);
    }
    
    @Override
    public Iterador<T> iterator() {
        return crearIterador();
    }
}

Casos de uso: Recorrido de colecciones, resultados de consultas a bases de datos, recorrido de árboles

17. Mediador (Mediator)

// Define un objeto que centraliza la comunicación entre otros objetos sin que se referencien directamente
public class MediadorSalaChat {
    private Map<String, Usuario> usuarios = new HashMap<>();
    
    public void registrarUsuario(Usuario usuario) {
        usuarios.put(usuario.getNombre(), usuario);
        usuario.setMediador(this);
    }
    
    public void enviarMensaje(String remitente, String destinatario, String mensaje) {
        Usuario usuarioDestino = usuarios.get(destinatario);
        if (usuarioDestino != null) {
            usuarioDestino.recibirMensaje(remitente, mensaje);
        }
    }
}

Casos de uso: Salas de chat, controladroes MVC, sistemas de调度 de aeropuertos

18. Memento (Memento)

// Captura y externaliza el estado interno de un objeto para que pueda ser restaurado más tarde
public class Memento {
    private String estado;
    
    public Memento(String estado) {
        this.estado = estado;
    }
    
    public String obtenerEstado() {
        return estado;
    }
}

public class Origenador {
    private String estado;
    
    public void establecerEstado(String estado) {
        this.estado = estado;
    }
    
    public String obtenerEstado() {
        return estado;
    }
    
    public Memento crearMemento() {
        return new Memento(estado);
    }
    
    public void restaurarDesdeMemento(Memento memento) {
        this.estado = memento.obtenerEstado();
    }
}

Casos de uso: Operaciones de deshacer, guardado de partidas de juegos, rollback de transacciones

19. Observador (Observer)

// Define una dependencia uno a muchos entre objetos de forma que cuando uno cambia de estado, todos sus dependientes son notificados
public interface Observador {
    void actualizar(String mensaje);
}

public class SujetoObservable {
    private List<Observador> observadores = new ArrayList<>();
    private String estado;
    
    public void agregarObservador(Observador observador) {
        observadores.add(observador);
    }
    
    public void notificarObservadores(String mensaje) {
        for (Observador observador : observadores) {
            observador.actualizar(mensaje);
        }
    }
    
    public void cambiarEstado(String nuevoEstado) {
        this.estado = nuevoEstado;
        notificarObservadores("El estado ha cambiado a: " + estado);
    }
}

Casos de uso: Escuchadores de eventos, colas de mensajes, publicador-suscriptor

20. Estado (State)

// Permite que un objeto cambie su comportamiento cuando cambia su estado interno
public interface EstadoMaquina {
    void insertarMoneda(MaquinaExpendedora maquina);
    void seleccionarProducto(MaquinaExpendedora maquina);
    void dispensarProducto(MaquinaExpendedora maquina);
}

public class EstadoSinMoneda implements EstadoMaquina {
    public void insertarMoneda(MaquinaExpendedora maquina) {
        maquina.setEstado(maquina.getEstadoConMoneda());
    }
    
    public void seleccionarProducto(MaquinaExpendedora maquina) {
        System.out.println("Por favor, inserte una moneda primero");
    }
    
    public void dispensarProducto(MaquinaExpendedora maquina) {
        System.out.println("No se ha insertado ninguna moneda");
    }
}

Casos de uso: Motores de flujos de trabajo, estados de personajes en juegos, estados de pedidos

21. Estrategia (Strategy)

// Define una familia de algoritmos, encapsula cada uno y los hace intercambiables
public interface EstrategiaPago {
    void pagar(double cantidad);
    void procesarPago(double cantidad);
}

public class ContextoPago {
    private EstrategiaPago estrategia;
    
    public void establecerEstrategia(EstrategiaPago estrategia) {
        this.estrategia = estrategia;
    }
    
    public void realizarPago(double cantidad) {
        estrategia.pagar(cantidad);
    }
}

Casos de uso: Métodos de pago, algoritmos de ordenamiento, algoritmos de compresión

22. Método Plantilla (Template Method)

// Define el esqueleto de un algoritmo en una operación, delegando algunos pasos a las subclases
public abstract class Proceso {
    protected abstract void inicializar();
    protected abstract void procesar();
    protected abstract void finalizar();
    
    // El método plantilla final
    public void ejecutar() {
        inicializar();
        procesar();
        finalizar();
    }
}

public class ProcesoConcreto extends Proceso {
    protected void inicializar() {
        System.out.println("Inicializando recursos...");
    }
    
    protected void procesar() {
        System.out.println("Procesando datos...");
    }
    
    protected void finalizar() {
        System.out.println("Liberando recursos...");
    }
}

Casos de uso: Diseño de frameworks, flujos de trabajo, partes fijas de algoritmos

23. Visitante (Visitor)

// Representa una operación sobre objetos de una estructura sin cambiar sus clases
public interface Visitante {
    void visitar(ElementoA elemento);
    void visitar(ElementoB elemento);
}

public interface Elemento {
    void aceptar(Visitante visitante);
}

public class ElementoA implements Elemento {
    public void aceptar(Visitante visitante) {
        visitante.visitar(this);
    }
    
    public String operacionA() {
        return "Operación específica del Elemento A";
    }
}

Casos de uso: Recorrido de AST de compiladores, operaciones en sistemas de archivos, generación de informes

Guía de Selección de Patrones

Selección de patrones según el tipo de problema:

Tipo de Problema Patrones Recomendados
Creación de objetos compleja Método Fábrica, Fábrica Abstracta, Constructor
Necesidad de única instancia Singleton
Interfaces incompatibles Adaptador
Necesidad de agregar funcionalidad dinámicamente Decorador
Simplificación de sistemas complejos Fachada
Algoritmos intercambiables Estrategia
El estado afecta el comportamiento Estado
Comunicación compleja entre objetos Mediador
Necesidad de operacioens de deshacer Memento
Dependencias uno a muchos Observador
Recorrido de colecciones Iterador
Operaciones sobre múltiples tipos Visitante

Experiencia en Desarrollo Real:

  1. No sobre-diseñar: Comienza con lo simple e introduce patrones cuando sea necesario
  2. Combinación de patrones: Los sistemas reales suelen combinar múltiples patrones
  3. Entender la esencia: Los patrones son conceptos, no plantillas de código fijas
  4. Diseño evolutivo: Introduce patrones gradualmente a medida que aumenta la complejidad del sistema

Etiquetas: patrones-de-diseño diseño-de-software arquitectura-de-software POO java

Publicado el 6-6 19:26