En está ocasión veremos qué son los patrones de diseño.
Según la página de Refactoring Guru, los patrones de diseño (design patterns) son:
...soluciones habituales a problemas comunes en el diseño de software. Cada patrón es como un plano que se puede personalizar para resolver un problema de diseño particular de tu código.
Patrones de diseño en Java
Observemos la siguiente imagen:
Podemos ver tres tipos y sus divisiones.
- Patrón Creacional: proporcionan varios mecanismos de creación de objetos que incrementan la flexibilidad y la reutilización del código existente.
- Patrón Estructural: estructurales explican cómo ensamblar objetos y clases en estructuras más grandes, a la vez que se mantiene la flexibilidad y eficiencia de estas estructuras.
- De comportamiento: los que tratan con algoritmos y la asignación de responsabilidades entre objetos.
Existen más patrones, pero solo nos enfocaremos en estos y sus divisiones.
Patrón creacional
Se enfoca en cómo se crean los objetos en un sistema. Su objetivo es controlar y optimizar la instanciación, haciendo el código más flexible y reutilizable.
Abstract factory
Crea familias de objetos relacionados.
interface Boton { void render(); } class BotonWindows implements Boton { public void render() { System.out.println("Botón estilo Windows"); } } class BotonMac implements Boton { public void render() { System.out.println("Botón estilo Mac"); } } interface GUIFactory { Boton crearBoton(); } class WindowsFactory implements GUIFactory { public Boton crearBoton() { return new BotonWindows(); } } class MacFactory implements GUIFactory { public Boton crearBoton() { return new BotonMac(); } } // Uso public class Main { public static void main(String[] args) { GUIFactory factory = new WindowsFactory(); Boton boton = factory.crearBoton(); boton.render(); } }
Builder
Construye objetos paso a paso.
class Casa { private String partes = ""; public void agregar(String parte) { partes += parte + " "; } public void mostrar() { System.out.println(partes); } } class ConstructorCasa { private Casa casa = new Casa(); public void construirPuerta() { casa.agregar("Puerta"); } public void construirVentana() { casa.agregar("Ventana"); } public Casa obtenerCasa() { return casa; } } // Uso public class Main { public static void main(String[] args) { ConstructorCasa builder = new ConstructorCasa(); builder.construirPuerta(); builder.construirVentana(); builder.obtenerCasa().mostrar(); } }
Factory method
Deja que las subclases decidan qué crear.
interface Transporte { void entregar(); } class Camion implements Transporte { public void entregar() { System.out.println("Entrega por carretera"); } } class Barco implements Transporte { public void entregar() { System.out.println("Entrega por mar"); } } abstract class Logistica { abstract Transporte crearTransporte(); } class LogisticaTerrestre extends Logistica { Transporte crearTransporte() { return new Camion(); } } // Uso public class Main { public static void main(String[] args) { Logistica logistica = new LogisticaTerrestre(); Transporte t = logistica.crearTransporte(); t.entregar(); } }
Prototype
Clona objetos existentes.
class Persona implements Cloneable { String nombre; int edad; public Persona(String nombre, int edad) { this.nombre = nombre; this.edad = edad; } public Persona clone() { try { return (Persona) super.clone(); } catch (CloneNotSupportedException e) { return null; } } } // Uso public class Main { public static void main(String[] args) { Persona original = new Persona("Ana", 25); Persona copia = original.clone(); System.out.println(copia.nombre + " " + copia.edad); } }
Singleton
Crea una sola instancia.
class Singleton { private static Singleton instancia; private Singleton() {} public static Singleton getInstancia() { if (instancia == null) { instancia = new Singleton(); } return instancia; } } // Uso public class Main { public static void main(String[] args) { Singleton obj1 = Singleton.getInstancia(); Singleton obj2 = Singleton.getInstancia(); System.out.println(obj1 == obj2); // true } }
Resumiendo:
- Abstract Factor: crea familias de objetos.
- Builder: construcción paso a paso.
- Factory Method: delega la creación.
- Prototype: clonación.
- Singleton: única instancia.
Patrón Estructural
Se centra en cómo se organizan y relacionan las clases y objetos para formar estructuras más grandes. Busca simplificar las relaciones y mejorar la composición del sistema.
Adapter
Permite que interfaces incompatibles trabajen juntas. Actúa como un “traductor” entre clases.
// Interfaz esperada interface EnchufeEuropeo { void conectar(); } // Clase incompatible class EnchufeAmericano { public void connect() { System.out.println("Conectado al enchufe americano"); } } // Adaptador class Adaptador implements EnchufeEuropeo { private EnchufeAmericano americano; public Adaptador(EnchufeAmericano americano) { this.americano = americano; } public void conectar() { americano.connect(); // Traduce la llamada } } // Uso public class Main { public static void main(String[] args) { EnchufeAmericano americano = new EnchufeAmericano(); EnchufeEuropeo adaptado = new Adaptador(americano); adaptado.conectar(); } }
Decorator
Permite agregar funcionalidades a un objeto dinámicamente sin modificar su código.
// Interfaz base interface Cafe { String descripcion(); } // Implementación base class CafeSimple implements Cafe { public String descripcion() { return "Café simple"; } } // Decorador base abstract class CafeDecorator implements Cafe { protected Cafe cafe; public CafeDecorator(Cafe cafe) { this.cafe = cafe; } } // Decorador concreto class Leche extends CafeDecorator { public Leche(Cafe cafe) { super(cafe); } public String descripcion() { return cafe.descripcion() + " con leche"; } } // Otro decorador class Azucar extends CafeDecorator { public Azucar(Cafe cafe) { super(cafe); } public String descripcion() { return cafe.descripcion() + " con azúcar"; } } // Uso public class Main { public static void main(String[] args) { Cafe cafe = new CafeSimple(); cafe = new Leche(cafe); cafe = new Azucar(cafe); System.out.println(cafe.descripcion()); } }
Resumiendo:
- Adapter: hace compatibles interfaces distintas.
- Decorator: añade funcionalidades sin modificar la clase original.
De comportamiento
Se ocupa de cómo interactúan y se comunican los objetos entre sí. Ayuda a definir responsabilidades y flujos de comunicación claros.
Value object
Objeto inmutable que representa un valor, no una identidad. Se compara por contenido, no por referencia.
class Dinero {class Dinero; public Dinero(double cantidad) { this.cantidad = cantidad; } public double getCantidad() { return cantidad; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Dinero)) return false; Dinero otro = (Dinero) obj; return Double.compare(otro.cantidad, cantidad) == 0; } } // Uso Dinero d1 = new Dinero(100); Dinero d2 = new Dinero(100); System.out.println(d1.equals(d2)); // true
Null object
Evita null usando un objeto con comportamiento “vacío”.
interface Cliente { String getNombre(); } class ClienteReal implements Cliente { private String nombre; public ClienteReal(String nombre) { this.nombre = nombre; } public String getNombre() { return nombre; } } class ClienteNulo implements Cliente { public String getNombre() { return "Cliente desconocido"; } } // Uso Cliente cliente = new ClienteNulo(); System.out.println(cliente.getNombre());
Strategy
Permite cambiar el algoritmo en tiempo de ejecución.
interface PagoStrategy { void pagar(int cantidad); } class PagoTarjeta implements PagoStrategy { public void pagar(int cantidad) { System.out.println("Pagado con tarjeta: " + cantidad); } } class PagoEfectivo implements PagoStrategy { public void pagar(int cantidad) { System.out.println("Pagado en efectivo: " + cantidad); } } class Carrito { private PagoStrategy estrategia; public void setEstrategia(PagoStrategy estrategia) { this.estrategia = estrategia; } public void pagar(int cantidad) { estrategia.pagar(cantidad); } } // Uso Carrito c = new Carrito(); c.setEstrategia(new PagoTarjeta()); c.pagar(100);
Command
Encapsula una solicitud como un objeto.
interface Command { void ejecutar(); } class Luz { public void encender() { System.out.println("Luz encendida"); } } class EncenderLuzCommand implements Command { private Luz luz; public EncenderLuzCommand(Luz luz) { this.luz = luz; } public void ejecutar() { luz.encender(); } } // Uso Luz luz = new Luz(); Command comando = new EncenderLuzCommand(luz); comando.ejecutar();
Chain responsability
Pasa una solicitud por una cadena de objetos hasta que alguien la maneje.
abstract class Handler { protected Handler siguiente; public void setSiguiente(Handler siguiente) { this.siguiente = siguiente; } public abstract void manejar(int nivel); } class SoporteBasico extends Handler { public void manejar(int nivel) { if (nivel <= 1) { System.out.println("Soporte básico resolvió"); } else if (siguiente != null) { siguiente.manejar(nivel); } } } class SoporteAvanzado extends Handler { public void manejar(int nivel) { if (nivel > 1) { System.out.println("Soporte avanzado resolvió"); } } } // Uso Handler basico = new SoporteBasico(); Handler avanzado = new SoporteAvanzado(); basico.setSiguiente(avanzado); basico.manejar(2);
Dependency injection
Las dependencias se inyectan en lugar de crearse dentro de la clase.
class Motor { public void encender() { System.out.println("Motor encendido"); } } class Auto { private Motor motor; // Inyección por constructor public Auto(Motor motor) { this.motor = motor; } public void arrancar() { motor.encender(); } } // Uso Motor motor = new Motor(); Auto auto = new Auto(motor); // se inyecta la dependencia auto.arrancar();
Resumen:
- Value Object: objeto inmutable basado en valor.
- Null Object: evita null con comportamiento vacío.
- Strategy: intercambia algoritmos.
- Command: encapsula acciones.
- Chain: pasa solicitudes en cadena.
- DI: inyecta dependencias externas
Importancia de los patrones de diseño
Ayudan a resolver problemas comunes de forma probada y reutilizable, además hacen que el código sea más limpio, flexible y profesional.
- Mejoran la organización del código.
- Facilitan el mantenimiento y la escalabilidad.
- Promueven buenas prácticas de desarrollo.
- Permiten que otros desarrolladores entiendan más rápido el sistema.
Hemos visto los patrones de diseños más usados en Java.
Enlaces:
https://codemonkeyjunior.blogspot.com/2013/12/patrones-de-diseno.htmlhttps://refactoring.guru/
https://martinfowler.com/articles/refactoring-2nd-ed.html


Comentarios
Publicar un comentario