O padrón Ponte, tamén coñecido como Bridge, é un padrón, pertencente á categoría de padróns estruturais, empregado na enxeñaría do software. Con el pretendese separar unha abstracción da súa implementación, desta forma poderanse empregar implementacións alternativas.
O padrón Ponte pode ser moi útil cando as implementacións varian a miúdo. Nese caso, as características da programación orientada a obxectos, tórnanse moi útiles, podendo facer cambios no código cun mínimo coñecemento previo do programa.
Obxectivos
Desacoplar unha abstracción da súa implementación de forma que ambas poidan variar independentemente.
Illamento mais aló da encapsulación.
Aplicabilidade
Podemos empregar o padró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 padró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
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 prové só de operacións primitivas, e Abstraction define operacións de alto nivel baseadas nestas primitivas.
Implementador Concreto (ConcreteImplementor): as clases implementadas
Consecuencias
Desacoplamento do obxecto interface e implementación: unha 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
O cliente non quere lidiar cos detalles dependentes da plataforma. O padró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
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 padró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ó padró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 padróns
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 padró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 padrón Fábrica Abstracta para crear os obxectos implementación.
Ponte semella o padrón adaptador , pero mentres que o padrón Adaptador intenta que as interfaces dunha ou máis 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 substituír as súas implementacions sen cambiar o código cliente.
Exemplo
/* * Memoria que permite o intercambio dos datos almacenados na mesma * */classMemoriaIntercambioextendsMemoria{publicMemoriaIntercambio(ImplementacionMemoriamemoria){super(memoria);}// Intercambia o dato almacenado na posición i, co almacenado na posición jpublicvoidintercambiar(Integeri,Integerj){Stringaux=super.obtener(i);super.guardar(i,super.obtener(j));super.guardar(j,aux);}}
/* * Memoria que unicamente permite obter e gardar datos. * */classMemoria{publicMemoria(ImplementacionMemoriamemoria){_imp=memoria;}//Obtén o dato da posición ipublicStringobtener(Integeri){return_imp.obtener(i);}// Garda o dato na posición ipublicvoidguardar(Integeri,Stringdato){_imp.guardar(i,dato);}// atributo privado coa implementación específica da memoriaprivateImplementacionMemoria_imp;}
/** * Clase Main do exemplo do padrón Ponte (Bridge) * */publicclassmain{/** * @param args */staticpublicvoidmain(Stringargv[]){ImplementacionMemoriamhash=newImplementacionHash();ImplementacionMemoriamvector=newImplementacionVector();Memoriam=newMemoria(mhash);MemoriaIntercambiomi=newMemoriaIntercambio(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. * */importjava.util.Vector;classImplementacionVectorimplementsImplementacionMemoria{publicImplementacionVector(){_mem=newVector<String>();}//Obtén o dato da posición ipublicStringobtener(Integeri){return_mem.get(i.intValue());}// Garda o dato na posición ipublicvoidguardar(Integeri,Stringdato){if(_mem.size()<=i){while(_mem.size()<=i){_mem.add("nil");}}_mem.set(i.intValue(),dato);}privateVector<String>_mem;}
/* * Interface que debe de ser implementada, polas implementacións específicas da memoria * */interfaceImplementacionMemoria{//Obtén o dato da posición ipublicStringobtener(Integeri);// Guarda el dato en la posición ipublicvoidguardar(Integeri,Stringdato);}
/* * Implementación de Memoria usando HashMap. * */importjava.util.HashMap;classImplementacionHashimplementsImplementacionMemoria{publicImplementacionHash(){_mem=newHashMap<Integer,String>();}//Obtén o dato da posición ipublicStringobtener(Integeri){return_mem.get(i);}// Garda o dato na posición ipublicvoidguardar(Integeri,Stringdato){_mem.put(i,dato);}privateHashMap<Integer,String>_mem;}