Bridge (patrón de deseño)
O patrón Ponte, tamén coñecido como Bridge, é un patrón, pertencente á categoría de patróns estruturais, empregado na enxeñaría do software. Con él pretendese separar unha abstracción da súa implementación, desta forma poderanse empregar implementacións alternativas.
O patrón Ponte pode ser moi útil cando as implementacións varian a miúdo. Nese caso, as características da programación orientada a obxetos, tórnanse moi útiles, podendo facer cambios no código cun mínimo coñecemento previo do programa.
Índice |
Obxectivos [editar]
- Desacoplar unha abstracción da súa implementación de forma que ambas poidan variar independentemente.
- Illamento mais aló da encapsulación.
Aplicabilidade [editar]
Podemos empregar o patrón Ponte cando:
- Queremos evitar unha ligazón permanente entre a abstracción e a súa implementación, podendo ser debido a que a implementación debe ser seleccionada ou cambiada en tempo de execución.
- Tanto as abstracción coma as súas implementación deben ser extensibles por medio de subclases. Neste caso, o patrón Ponte permite combinar abstracción e implementación diferentes e estendelas independentemente.
- Os cambios na implementación dunha abstracción non deben impactar nos clientes,o seu código non ten que ser recompilado.
- Queremos ocultar a implementación dunha abstracción completamente ós clientes.
- Deséxase compartir unha implementación entre múltiples obxectos, e isto é ocultado ós clientes.
Participantes [editar]
- Abstracción (Abstraction): define a clase interface. Mantén unha referencia ó obxecto implementador.
- Abstracción refinada (RefinedAbstraction): estende e implementa a interface Abtracción.
- Implementador (Implementor): define a interface para as clases implementación. Esta interface non ten que corresponderse exactamente coa interface de Abstraction, as das interfaces poden ser bastante diferentes. Tipicamente a interface Implementor provee só operacións primitivas, e Abstraction define operacións de alto nivel baseadas nestas primitivas.
- Implementador Concreto (ConcreteImplementor): as clases implementadas
Consecuencias [editar]
- Desacoplamento do obxecto interface e implementación: una implementación non é limitada permanentemente a unha interface. A implementación dunha abstracción pode configurarse en tempo de execución. Ademais, un obxecto, ten a posibilidade de cambiar a súa implementación en tempo de execución. Desacopla Abstraction e Implementor tamén elimina as dependencias sobre a implementación en tempo de compilación. Cambiar unha clase de implementación non require recompilar a clase Abstraction nin os seus clientes. Esta propiedade é esencial cando hai que asegurar a compatibilidade binaria entre diferentes versións dunha biblioteca de clases, fomentando as capas, que nos leven a un nivel mellor estruturado.
- Mellorar a estensibilidade: Pódese estender as xerarquías de abstracción e implementación de forma independente.
Estrutura [editar]
O cliente non quere lidiar cos detalles dependentes da plataforma. O patrón Ponte encapsula estas complexidades tras unha capa abstracta.
Ponte enfatiza identificar e desacoplar a abstracción de interface da abstracción da implementación.
Implementación [editar]
- Só un Implementador: en situación onde existe só unha implementación, non é preciso crear una clase Implementor abstracta, sendo este, un caso especial do patrón no que hai unha relación un-a-un entre Abstraction e Implementor. Non obstante, esta separación é moi útil cando un cambio na implementación dunha clase non debe afectar os seus clientes.
- Implemementador: Sendo esta unha das principais diferenzas có patrón estratexia, se a abstracción coñece a xerarquía de implementadores pode crear o implementador no construtor, podendo decidir cal instanciar dependendo dos parámetros do construtor. O inconveniente ven dado pola dependencia da xerarquía, se aparece un novo teremos que modificar a abstracción.
Outra aproximación é delegar noutro obxecto (singleton), este encargarase de proporciona o implementador concreto da abstracción.
- Compartir implementadores.
- Empregando herdanza múltiple.
Ponte con outros patróns [editar]
- Adaptador fai que as cousas funcionen despois de ser deseñadas, Ponte fai que as cousas funcionen antes de ser deseñadas.
- Ponte é deseñado para permitir que a abstracción e a implementación varíen de forma independente.Adaptador é deseñado para que clases non relacionadas traballen xuntas.
- Estado, Estratexia, Ponte (e algúns casos de Adaptador) teñen solucións estruturais semellantes. Diferéncianse na intención, resolven diferentes problemas.A estrutura de Estado e de Ponte é idéntica (excepto que Ponte admite xerarquías de herdanza envolventes, mentres que Estado só permite unha). Os dous patróns usan a mesma estrutura para resolver diferentes problemas: Estado permite cambiar o comportamento dun obxecto o cambiar o seu estado, mentres que Ponte pretende desacoplar a abstracción da súa implementación, podendo variar estas dúas de forma independiente.
- Se a clase interface delega a creación a clase implementación, entón o deseño usualmente emprega o patrón Fábrica Abstracta para crear os obxectos implementación.
- Ponte semella o patrón adaptador , pero mentres que o patrón Adaptador intenta que as interfaces dunha ou mais clases sexa a mesma que para unha clase particular, Ponte está deseñado para separar as clases interfaces da súa implementación, desta forma poder variar e reemplazar as súas implementacions sen cambiar o código cliente.
Exemplo [editar]
/* * Memoria que permite o intercambio dos datos almacenados na mesma * */ class MemoriaIntercambio extends Memoria{ public MemoriaIntercambio(ImplementacionMemoria memoria){ super(memoria); } // Intercambia o dato almacenado na posición i, co almacenado na posición j public void intercambiar(Integer i, Integer j){ String aux = super.obtener(i); super.guardar(i, super.obtener(j)); super.guardar(j, aux); } }
/* * Memoria que unicamente permite obter e gardar datos. * */ class Memoria { public Memoria(ImplementacionMemoria memoria){ _imp = memoria; } //Obtén o dato da posición i public String obtener(Integer i) { return _imp.obtener(i); } // Garda o dato na posición i public void guardar(Integer i, String dato){ _imp.guardar(i, dato); } // atributo privado coa implementación específica da memoria private ImplementacionMemoria _imp; }
/** * Clase Main do exemplo do patrón Ponte (Bridge) * */ public class main { /** * @param args */ static public void main(String argv[]) { ImplementacionMemoria mhash = new ImplementacionHash(); ImplementacionMemoria mvector = new ImplementacionVector(); Memoria m = new Memoria(mhash); MemoriaIntercambio mi = new MemoriaIntercambio(mvector); m.guardar(0, "0xfa21"); m.guardar(1, "0x8732"); m.guardar(2, "0x329f"); mi.guardar(0, "0xfa21"); mi.guardar(1, "0x8732"); mi.intercambiar(0, 1); System.out.println(m.obtener(1)); System.out.println(mi.obtener(0)); } }
/* * Implementación de Memoria usando Vector. * */ import java.util.Vector; class ImplementacionVector implements ImplementacionMemoria { public ImplementacionVector() { _mem = new Vector<String>(); } //Obtén o dato da posición i public String obtener(Integer i) { return _mem.get(i.intValue()); } // Garda o dato na posición i public void guardar(Integer i, String dato) { if (_mem.size() <= i){ while (_mem.size() <= i){ _mem.add("nil"); } } _mem.set(i.intValue(), dato); } private Vector<String> _mem; }
/* * Interface que debe de ser implementada, polas implementacións específicas da memoria * */ interface ImplementacionMemoria { //Obtén o dato da posición i public String obtener(Integer i); // Guarda el dato en la posición i public void guardar(Integer i, String dato); }
/* * Implementación de Memoria usando HashMap. * */ import java.util.HashMap; class ImplementacionHash implements ImplementacionMemoria { public ImplementacionHash() { _mem = new HashMap<Integer, String>(); } //Obtén o dato da posición i public String obtener(Integer i) { return _mem.get(i); } // Garda o dato na posición i public void guardar(Integer i, String dato) { _mem.put(i, dato); } private HashMap<Integer, String> _mem; }
