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