Qual è la differenza tra i modelli Bridge e Adapter?
Qual è la differenza tra i modelli Bridge e Adapter?
Risposte:
"Adapter fa funzionare le cose dopo che sono state progettate; Bridge le fa funzionare prima che siano. [GoF, p219]"
In effetti, il pattern Adapter è utile quando si dispone di codice esistente, sia esso di terze parti o interno, ma fuori dal proprio controllo o altrimenti non modificabile per soddisfare completamente l'interfaccia di cui si ha bisogno. Ad esempio, abbiamo un SuperWeaponsArray che può controllare una vasta gamma di dispositivi del giorno del giudizio.
public class SuperWeaponsArray {
/*...*/
public void destroyWorld() {
for (Weapon w : armedWeapons) {
w.fire();
}
}
}
Grande. Solo che ci rendiamo conto di avere un dispositivo nucleare nel nostro arsenale che precede di gran lunga la conversione all'interfaccia delle armi. Ma ci piacerebbe davvero che funzionasse qui ... quindi cosa facciamo ... incastrarlo!
NukeWeaponsAdaptor - basato sulla nostra classe Nuke, ma che esporta l'interfaccia Weapon. Dolce, ora possiamo sicuramente distruggere il mondo. Sembra un po 'un disastro, ma fa funzionare le cose.
Il ponte modello è qualcosa che implementi in anticipo: se sai di avere due gerarchie ortogonali, fornisce un modo per disaccoppiare l'interfaccia e l'implementazione in modo tale da non ottenere un numero folle di classi. Diciamo che hai:
Tipi di oggetti file MemoryMappedFile e DirectReadFile. Supponiamo che tu voglia essere in grado di leggere file da varie fonti (forse Linux vs implementazioni Windows, ecc.). Bridge ti aiuta a evitare di finire con:
MemoryMappedWindowsFile MemoryMappedLinuxFile DirectReadWindowsFile DirectReadLinuxFile
http://en.wikipedia.org/wiki/Adapter_pattern
Il pattern Adapter riguarda più il far funzionare il codice esistente con un sistema o un'interfaccia più recenti.
Se disponi di un set di API di servizi Web standard dell'azienda che desideri offrire all'interfaccia di estensibilità esistente di un'altra applicazione, potresti prendere in considerazione la possibilità di scrivere un set di adattatori per farlo. Nota che c'è un'area grigia e questo riguarda più come definisci tecnicamente il modello, poiché altri modelli come la facciata sono simili.
http://en.wikipedia.org/wiki/Bridge_pattern
Il modello Bridge ti consentirà di avere implementazioni alternative di un algoritmo o di un sistema.
Sebbene non sia un classico esempio di pattern Bridge, immagina di avere alcune implementazioni di un data store: una è efficiente nello spazio, l'altra è efficiente nelle prestazioni grezze ... e hai un business case per offrire entrambe nella tua app o framework .
Per quanto riguarda la tua domanda, "dove posso usare quale schema", la risposta è, ovunque abbia senso per il tuo progetto! Forse prendi in considerazione l'idea di offrire una modifica di chiarimento per guidare la discussione su dove ritieni di dover utilizzare l'uno o l'altro.
Adattatore:
Diagramma UML: dall'articolo dofactory :
Target : definisce l'interfaccia specifica del dominio utilizzata dal Cliente.
Adapter : adatta l'interfaccia Adaptee all'interfaccia Target.
Adaptee : definisce un'interfaccia esistente che deve essere adattata.
Cliente : collabora con oggetti conformi all'interfaccia Target.
Esempio:
Quadrato e Rettangolo sono due forme diverse e ottenere l'area () di ciascuna di esse richiede metodi diversi. Ma ancora Square lavora sull'interfaccia Rectangle con la conversione di alcune delle proprietà.
public class AdapterDemo{
public static void main(String args[]){
SquareArea s = new SquareArea(4);
System.out.println("Square area :"+s.getArea());
}
}
class RectangleArea {
public int getArea(int length, int width){
return length * width;
}
}
class SquareArea extends RectangleArea {
int length;
public SquareArea(int length){
this.length = length;
}
public int getArea(){
return getArea(length,length);
}
}
Ponte:
EDIT: (come da suggerimento @quasoft)
Hai quattro componenti in questo modello.
Astrazione : definisce un'interfaccia
RefinedAbstraction : implementa l'astrazione:
Implementor : definisce un'interfaccia per l'implementazione
ConcreteImplementor : implementa l'interfaccia Implementor.
Snippet di codice:
Gear gear = new ManualGear();
Vehicle vehicle = new Car(gear);
vehicle.addGear();
gear = new AutoGear();
vehicle = new Car(gear);
vehicle.addGear();
Post correlato:
Quando usi il Bridge Pattern? In cosa differisce dal pattern Adapter?
Le differenze principali: da sourcemaking articolo
Questo post è in circolazione da un po 'di tempo. Tuttavia, è importante capire che una facciata è in qualche modo simile a un adattatore, ma non è proprio la stessa cosa. Un adattatore "adatta" una classe esistente a una classe client solitamente non compatibile. Supponiamo che tu abbia un vecchio sistema di flusso di lavoro che la tua applicazione sta utilizzando come client. La tua azienda potrebbe eventualmente sostituire il sistema di workflow con uno nuovo "incompatibile" (in termini di interfacce). Nella maggior parte dei casi, è possibile utilizzare il pattern dell'adattatore e scrivere codice che richiami effettivamente le interfacce del nuovo motore del flusso di lavoro. Un bridge viene generalmente utilizzato in modo diverso. Se si dispone effettivamente di un sistema che deve funzionare con diversi file system (cioè disco locale, NFS, ecc.) È possibile utilizzare il pattern bridge e creare un livello di astrazione per lavorare con tutti i file system. Questo sarebbe fondamentalmente un semplice caso d'uso per il modello del ponte. La facciata e l'adattatore condividono alcune proprietà male facciate vengono solitamente utilizzate per semplificare un'interfaccia / classe esistente . Agli albori degli EJB non c'erano chiamate locali per EJB. Gli sviluppatori hanno sempre ottenuto lo stub, lo hanno ristretto e lo hanno chiamato "pseudo-remoto". Questo spesso causava problemi di prestazioni (specialmente quando si chiamava veramente via cavo). Gli sviluppatori esperti utilizzerebbero il modello di facciata per fornire un'interfaccia a grana grossa al cliente. Questa facciata, a sua volta, eseguirà più chiamate a diversi metodi più dettagliati. Tutto sommato, ciò ha notevolmente ridotto il numero di chiamate di metodo richieste e aumentato le prestazioni.
Bridge è un adattatore migliorato. Bridge include l'adattatore e aggiunge ulteriore flessibilità ad esso. Ecco come gli elementi della risposta di Ravindra si mappa tra i modelli:
Adapter | Bridge
-----------|---------------
Target | Abstraction
-----------|---------------
| RefinedAbstraction
|
| This element is Bridge specific. If there is a group of
| implementations that share the same logic, the logic can be placed here.
| For example, all cars split into two large groups: manual and auto.
| So, there will be two RefinedAbstraction classes.
-----------|---------------
Adapter | Implementor
-----------|---------------
Adaptee | ConcreteImplementor
Nella risposta in alto, @James cita una frase dal GoF, pagina 219. Penso che valga la pena riprodurre qui la spiegazione completa.
Adattatore contro Bridge
I pattern Adapter e Bridge hanno alcuni attributi comuni. Entrambi promuovono la flessibilità fornendo un livello di riferimento indiretto a un altro oggetto. Entrambi implicano l'inoltro di richieste a questo oggetto da un'interfaccia diversa dalla propria.
La differenza fondamentale tra questi modelli risiede nei loro intenti. Adapter si concentra sulla risoluzione delle incompatibilità tra due interfacce esistenti. Non si concentra su come queste interfacce vengono implementate, né considera come potrebbero evolversi in modo indipendente. È un modo per far lavorare insieme due classi progettate in modo indipendente senza reimplementare l'una o l'altra. Bridge, d'altra parte, collega un'astrazione e le sue (potenzialmente numerose) implementazioni. Fornisce un'interfaccia stabile ai client anche se consente di variare le classi che lo implementano. Ospita anche nuove implementazioni man mano che il sistema si evolve.
Come risultato di queste differenze, Adapter e Bridge vengono spesso utilizzati in punti diversi del ciclo di vita del software. Un adattatore diventa spesso necessario quando si scopre che due classi incompatibili dovrebbero lavorare insieme, generalmente per evitare la replica del codice. L'accoppiamento è imprevisto. Al contrario, l'utente di un bridge capisce subito che un'astrazione deve avere diverse implementazioni ed entrambe possono evolversi indipendentemente. Il pattern Adapter fa funzionare le cose dopo che sono state progettate; Il bridge li fa funzionare prima che lo siano. Ciò non significa che Adapter sia in qualche modo inferiore a Bridge; ogni modello risolve semplicemente un problema diverso.
Supponi di avere una classe Shape astratta con una funzionalità di disegno (generica / astratta) e un Cerchio che implementa la Forma. Il pattern Bridge è semplicemente un approccio di astrazione a due vie per disaccoppiare l'implementazione (disegno in Circle) e la funzionalità generica / astratta (disegno nella classe Shape).
Cosa significa veramente? A prima vista, sembra qualcosa che stai già facendo (per inversione di dipendenza). Quindi non preoccuparti di avere una base di codice meno rigida o più modulare. Ma c'è una filosofia un po 'più profonda dietro.
Dalla mia comprensione, la necessità di un modello di utilizzo potrebbe emergere quando ho bisogno di aggiungere nuove classi che sono strettamente correlate al sistema corrente (come RedCircle o GreenCircle) e che differiscono solo per una singola funzionalità (come il colore). E avrò bisogno del pattern Bridge in particolare se le classi di sistema esistenti (Circle o Shape) devono essere cambiate frequentemente e non vuoi che le nuove classi aggiunte siano influenzate da tali modifiche. Ecco perché la funzionalità di disegno generica viene astratta in una nuova interfaccia in modo da poter modificare il comportamento del disegno indipendentemente da Forma o Cerchio.