Patrones de diseño en Java: guía completa con ejemplos
Los patrones de diseño (design patterns) son soluciones probadas para problemas típicos que surgen durante el desarrollo de software. En Java, como lenguaje orientado a objetos fuertemente tipado, los patrones son especialmente efectivos. Ayudan a que el código sea flexible, reutilizable y fácil de mantener. En este artículo, analizaremos los tres grupos principales de patrones (según la clasificación GoF): creacionales, estructurales y de comportamiento, con ejemplos reales en Java.
1. Patrones creacionales (Creational Patterns)
Estos patrones gestionan el proceso de creación de objetos, haciendo que el sistema sea independiente de cómo se crean, componen y representan los objetos.
1.1. Singleton (Instancia única)
Garantiza que una clase tenga solo una instancia y proporciona un punto de acceso global a ella. Un ejemplo clásico es un pool de conexiones a la base de datos o un logger.
public class DatabaseConnection { private static volatile DatabaseConnection instance; private String connectionString;
private DatabaseConnection(String connectionString) { this.connectionString = connectionString; }
public static DatabaseConnection getInstance(String connectionString) { if (instance == null) { synchronized (DatabaseConnection.class) { if (instance == null) { instance = new DatabaseConnection(connectionString); } } } return instance; }
public void connect() { System.out.println("Connected to: " + connectionString); }}
// UsoDatabaseConnection conn = DatabaseConnection.getInstance("jdbc:mysql://localhost:3306/mydb");conn.connect();1.2. Factory Method (Método de fábrica)
Define una interfaz para crear un objeto, pero permite que las subclases alteren el tipo de objeto que se crea. Es ideal cuando el tipo exacto del objeto no se conoce de antemano.
interface Transport { void deliver();}
class Truck implements Transport { public void deliver() { System.out.println("Entrega en camión por tierra"); }}
class Ship implements Transport { public void deliver() { System.out.println("Entrega en barco por mar"); }}
abstract class Logistics { abstract Transport createTransport(); public void planDelivery() { Transport t = createTransport(); t.deliver(); }}
class RoadLogistics extends Logistics { Transport createTransport() { return new Truck(); }}
class SeaLogistics extends Logistics { Transport createTransport() { return new Ship(); }}
// UsoLogistics logistics = new RoadLogistics();logistics.planDelivery(); // Salida: Entrega en camión por tierra2. Patrones estructurales (Structural Patterns)
Se encargan de la composición de clases y objetos, ayudando a construir estructuras complejas a partir de componentes simples.
2.1. Adapter (Adaptador)
Permite que objetos con interfaces incompatibles trabajen juntos. Se usa a menudo para integrar código antiguo (legacy) con código nuevo.
// Interfaz antiguainterface OldSystem { String getDataInXML();}
// Interfaz nuevainterface NewSystem { String getDataInJSON();}
// Implementación del sistema antiguoclass LegacySystem implements OldSystem { public String getDataInXML() { return "<data>Hello</data>"; }}
// Adaptadorclass XMLToJSONAdapter implements NewSystem { private OldSystem oldSystem; public XMLToJSONAdapter(OldSystem oldSystem) { this.oldSystem = oldSystem; } public String getDataInJSON() { String xml = oldSystem.getDataInXML(); return "{\\"data\\": \\"" + xml.replaceAll("<.*?>", "") + "\\"}"; }}
// Y