Patrones de diseño avanzados en Java: de Bridge a Visitor
\n\nLos patrones de diseño son soluciones probadas para problemas típicos de la arquitectura de software. Si los patrones básicos (Singleton, Factory, Observer) son conocidos por todo desarrollador Java, los patrones avanzados a menudo quedan fuera del foco. En este artículo analizaremos tres potentes patrones: Bridge, Visitor y Memento. Aprenderás cuándo aplicarlos y obtendrás ejemplos de código listos en Java.
\n\n1. Bridge (Puente) — separamos la abstracción de la implementación
\n\nEl patrón Bridge se utiliza cuando es necesario separar la abstracción y su implementación, para que puedan cambiar de forma independiente. Esto es especialmente útil cuando tienes varias dimensiones de cambio (por ejemplo, diferentes tipos de dispositivos y diferentes controles remotos).
\n\nCuándo aplicarlo: cuando quieres evitar una «explosión de clases» debido a la combinación de diferentes variantes.
\n\nEjemplo: Control de dispositivos
\n\nImagina que tenemos controles remotos (Remote) y dispositivos (Device). Queremos que un mismo control remoto pueda funcionar con diferentes dispositivos (TV, Radio).
\n\n// Implementación (Device)\ninterface Device {\n void turnOn();\n void turnOff();\n void setVolume(int percent);\n}\n\nclass TV implements Device {\n @Override\n public void turnOn() { System.out.println("TV encendido"); }\n @Override\n public void turnOff() { System.out.println("TV apagado"); }\n @Override\n public void setVolume(int percent) { System.out.println("Volumen TV: " + percent + "%"); }\n}\n\nclass Radio implements Device {\n @Override\n public void turnOn() { System.out.println("Radio encendido"); }\n @Override\n public void turnOff() { System.out.println("Radio apagado"); }\n @Override\n public void setVolume(int percent) { System.out.println("Volumen Radio: " + percent + "%"); }\n}\n\n// Abstracción (Remote)\nabstract class Remote {\n protected Device device;\n\n public Remote(Device device) {\n this.device = device;\n }\n\n public abstract void togglePower();\n public abstract void volumeUp();\n public abstract void volumeDown();\n}\n\nclass BasicRemote extends Remote {\n private boolean isOn = false;\n private int volume = 10;\n\n public BasicRemote(Device device) {\n super(device);\n }\n\n @Override\n public void togglePower() {\n if (isOn) { device.turnOff(); isOn = false; }\n else { device.turnOn(); isOn = true; }\n }\n\n @Override\n public void volumeUp() {\n volume += 5;\n device.setVolume(volume);\n }\n\n @Override\n public void volumeDown() {\n volume -= 5;\n device.setVolume(volume);\n }\n}\n\n// Uso\npublic class BridgeDemo {\n public static void main(String[] args) {\n Device tv = new TV();\n Remote remote = new BasicRemote(tv);\n remote.togglePower(); // TV encendido\n remote.volumeUp(); // Volumen TV: 15%\n\n Device radio = new Radio();\n remote = new BasicRemote(radio);\n remote.togglePower(); // Radio encendido\n remote.volumeUp(); // Volumen Radio: 15%\n }\n}\n\nVentaja: podemos agregar nuevos tipos de dispositivos o nuevos controles remotos sin modificar el código existente. El patrón Bridge es ideal para aplicaciones multiplataforma (por ejemplo, IU en diferentes SO).
\n\n2. Visitor (Visitante) — nuevas operaciones sin modificar las clases
\n\nEl patrón Visitor permite agregar nuevas operaciones a un grupo de objetos sin modificar las clases de esos objetos. Es una forma clásica de sortear las limitaciones del despacho simple en Java.
\n\nCuándo aplicarlo: cuando tienes una jerarquía de clases estable, pero agregas nuevas operaciones con frecuencia (por ejemplo, exportación a diferentes formatos, generación de informes).
\n\nEjemplo: Exportación de figuras a diferentes formatos
\n\nTenemos figuras