Quando usi il modello Bridge? In cosa differisce dal modello di adattatore?


154

Qualcuno ha mai usato Bridge Pattern in un'applicazione del mondo reale? Se è così, come l'hai usato? Sono io, o è solo il modello adattatore con una piccola iniezione di dipendenza gettata nel mix? Merita davvero il suo modello?


Si prega di considerare di accettare una risposta diversa a questa domanda. La risposta attualmente accettata è errata e inutile. Le risposte più recenti sono di gran lunga superiori.
jaco0646,

Il libro GoF risponde direttamente a questa domanda.
jaco0646,

Risposte:


76

Un classico esempio del modello Bridge viene utilizzato nella definizione delle forme in un ambiente UI (vedere la voce Wikipedia del modello Bridge ). Il modello Bridge è un composto dei modelli Template e Strategy .

È una visione comune alcuni aspetti del modello Adapter nel modello Bridge. Tuttavia, per citare da questo articolo :

A prima vista, il modello Bridge assomiglia molto al modello Adapter in quanto una classe viene utilizzata per convertire un tipo di interfaccia in un altro. Tuttavia, l'intento del modello Adapter è di rendere le interfacce di una o più classi uguali a quelle di una determinata classe. Il modello Bridge è progettato per separare l'interfaccia di una classe dalla sua implementazione in modo da poter variare o sostituire l'implementazione senza modificare il codice client.


1
Bridge non ha nulla a che fare con Template o Strategia. Il ponte è un modello strutturale. Il modello e la strategia sono modelli comportamentali.
jaco0646,

249

C'è una combinazione delle risposte di Federico e John .

Quando:

                   ----Shape---
                  /            \
         Rectangle              Circle
        /         \            /      \
BlueRectangle  RedRectangle BlueCircle RedCircle

Rifattore per:

          ----Shape---                        Color
         /            \                       /   \
Rectangle(Color)   Circle(Color)           Blue   Red

6
Perché dovresti ereditare i colori?
vainolo,

10
@vainolo perché Color è un'interfaccia e Blue, Red sono i colori concreti
Weltschmerz,

3
Questo è solo un refactoring. Intento del modello a ponte: "Disaccoppia un'astrazione dalla sua implementazione in modo che i due possano variare in modo indipendente." Dov'è l'astrazione e dov'è l'implementazione qui?
Clapas,

1
Rectangle (Color) non è più astratto della cosa BlueRectangle?
Anton Shchastnyi,

2
@clapas, Abstraction è "Shape.color" della proprietà, pertanto la classe Red e la classe Blue sono implementazioni e l'interfaccia Color è bridge.
reco

230

Il modello Bridge è un'applicazione del vecchio consiglio, "preferisci la composizione rispetto all'eredità". È utile quando devi sottoclassare diverse volte in modo ortogonale tra loro. Supponiamo che tu debba implementare una gerarchia di forme colorate. Non dovresti sottoclassare Shape con Rectangle e Circle e quindi sottoclassare Rectangle con RedRectangle, BlueRectangle e GreenRectangle e lo stesso per Circle, vero? Preferiresti dire che ogni forma ha un colore e implementare una gerarchia di colori, e questo è il modello a ponte. Bene, non implementerei una "gerarchia di colori", ma hai avuto l'idea ...


1
Vedi anche il diagramma di Anton Shchastnyi di seguito per un'illustrazione grafica di questa spiegazione.
NomadeNumerique,

2
Non credo che un colore sia un buon esempio per una gerarchia di implementazione, è piuttosto confuso. C'è un buon esempio del modello Bridge in "Pattern design" di GoF, in cui l'implementazione dipende dalla piattaforma: IBM PM, X UNIX ecc.
Clapas,

215

Quando:

        A
     /     \
    Aa      Ab
   / \     /  \
 Aa1 Aa2  Ab1 Ab2

Rifattore per:

     A         N
  /     \     / \
Aa(N) Ab(N)  1   2

3
Penso che sia un approccio molto pragmatico ai modelli: 1) descrivi un progetto subottimale diretto 2) il design / codice del refactor a uno meglio preso in considerazione
Alexey

1
Usa il concetto matematico per spiegare il modello di progettazione di Bridge. Molto interessato.
Jian Huang,

1
Questo è solo un refactoring. Intento del modello a ponte: "Disaccoppia un'astrazione dalla sua implementazione in modo che i due possano variare in modo indipendente." Dov'è l'astrazione e dov'è l'implementazione qui?
Clapas,

John lo inserisce bene in un post sul blog . Ho trovato una buona lettura per una panoramica di alto livello.
Vaibhav Bhalla,

29

Adapter e Bridge sono sicuramente correlati e la distinzione è sottile. È probabile che alcune persone che pensano di utilizzare uno di questi schemi stiano effettivamente utilizzando l'altro modello.

La spiegazione che ho visto è che Adapter viene utilizzato quando si tenta di unificare le interfacce di alcune classi incompatibili che già esistono . L'adattatore funziona come una sorta di traduttore per implementazioni che potrebbero essere considerate legacy .

Considerando che il modello Bridge viene utilizzato per il codice che ha più probabilità di essere greenfield. Stai progettando il Bridge per fornire un'interfaccia astratta per un'implementazione che deve variare, ma definisci anche l'interfaccia di quelle classi di implementazione.

I driver di dispositivo sono un esempio spesso citato di Bridge, ma direi che è un Bridge se si definiscono le specifiche dell'interfaccia per i fornitori di dispositivi, ma è un Adapter se si stanno prendendo i driver di dispositivo esistenti e si sta creando una classe wrapper fornire un'interfaccia unificata.

Quindi, per quanto riguarda il codice, i due modelli sono molto simili. Per quanto riguarda il business, sono diversi.

Vedi anche http://c2.com/cgi/wiki?BridgePattern


Ehi Bill. Non capisco perché utilizziamo necessariamente il modello Bridge nei driver di dispositivo. Voglio dire, possiamo delegare facilmente l'implementazione (di lettura, scrittura, ricerca ecc.) Alla classe corretta tramite il polimorfismo, giusto? O forse con un visitatore? Perché deve essere Bridge? Grazie in anticipo.
stdout,

1
@zgulser, sì, usi il polimorfismo. Il modello Bridge descrive un tipo di utilizzo di sottoclassi per disaccoppiare l'implementazione dall'astrazione.
Bill Karwin,

Intendevi disaccoppiare l'implementazione della forma (es. Rettangolo) da oggi astrazione del colore, giusto? E credo che tu stia dicendo che ci sono molti modi per farlo e Bridge è solo uno di questi.
stdout

Sì, la sottoclasse ha altri usi. Questo particolare modo di utilizzare le sottoclassi è ciò che lo rende il modello Bridge.
Bill Karwin,

E il disaccoppiamento intendo va dall'interfaccia Shape astratta a un'implementazione concreta di Rectangle. Quindi puoi scrivere codice che necessita di un oggetto del tipo "Shape", anche se l'oggetto concreto è in realtà una sottoclasse di Shape.
Bill Karwin,

27

Nella mia esperienza, Bridge è un modello abbastanza frequente, perché è la soluzione ogni volta che ci sono due dimensioni ortogonali nel dominio . Ad esempio forme e metodi di disegno, comportamenti e piattaforme, formati di file e serializzatori e così via.

E un consiglio: pensa sempre ai modelli di progettazione dal punto di vista concettuale , non dal punto di vista dell'implementazione. Dal giusto punto di vista, Bridge non può essere confuso con Adapter, perché risolvono un problema diverso e la composizione è superiore all'eredità non per il bene di se stessa, ma perché consente di gestire separatamente le preoccupazioni ortogonali.


22

L'intento di Bridge e Adapter è diverso e abbiamo bisogno di entrambi i modelli separatamente.

Modello del ponte:

  1. È un modello strutturale
  2. Astrazione e implementazione non sono vincolate al momento della compilazione
  3. Astrazione e implementazione: entrambi possono variare senza impatto sul client
  4. Usa la composizione sull'eredità.

Usa il modello Bridge quando:

  1. Desideri l'associazione runtime dell'implementazione,
  2. Hai una proliferazione di classi risultante da un'interfaccia accoppiata e numerose implementazioni,
  3. Vuoi condividere un'implementazione tra più oggetti,
  4. È necessario mappare le gerarchie di classi ortogonali.

La risposta di John Sonmez mostra chiaramente l'efficacia del modello a ponte nella riduzione della gerarchia di classi.

È possibile fare riferimento al seguente collegamento alla documentazione per ottenere una migliore comprensione del modello di ponte con un esempio di codice

Modello di adattatore :

  1. Esso permette due interfacce indipendenti di lavorare insieme attraverso i diversi oggetti, possibilmente giocando stesso ruolo.
  2. Modifica l'interfaccia originale.

Differenze chiave:

  1. L'adattatore fa funzionare le cose dopo che sono state progettate; Bridge li fa funzionare prima che lo siano.
  2. Bridge è progettato in anticipo per consentire all'astrazione e all'implementazione di variare in modo indipendente . L'adattatore è adattato per far lavorare insieme le classi non correlate.
  3. L'intento: l' adattatore consente a due interfacce non correlate di lavorare insieme. Bridge consente all'astrazione e all'implementazione di variare in modo indipendente.

Domanda SE relativa con diagramma UML e codice di lavoro:

Differenza tra il modello Bridge e il modello Adapter

Articoli utili:

articolo modello bridge di origine

articolo modello adattatore sorgente

articolo modello ponte journaldev

MODIFICARE:

Esempio del mondo reale di Bridge Pattern (Come suggerito da meta.stackoverflow.com, esempio di sito di documentazione incorporato in questo post poiché la documentazione sta per tramontare)

Il modello a ponte disaccoppia l'astrazione dall'implementazione in modo che entrambi possano variare in modo indipendente. È stato realizzato con la composizione piuttosto che con l'eredità.

Bridge pattern UML da Wikipedia:

Bridge pattern UML da Wikipedia

Hai quattro componenti in questo modello.

Abstraction: Definisce un'interfaccia

RefinedAbstraction: Implementa l'astrazione:

Implementor: Definisce un'interfaccia per l'implementazione

ConcreteImplementor: Implementa l'interfaccia dell'attuatore.

The crux of Bridge pattern :Due gerarchie di classi ortogonali che utilizzano la composizione (e nessuna eredità). La gerarchia di astrazione e la gerarchia di implementazione possono variare in modo indipendente. L'implementazione non si riferisce mai all'astrazione. L'astrazione contiene l'interfaccia di implementazione come membro (attraverso la composizione). Questa composizione riduce un ulteriore livello di gerarchia ereditaria.

Caso d'uso parola reale:

Consentire a veicoli diversi di avere entrambe le versioni del sistema di cambio manuale e automatico.

Codice di esempio:

/* Implementor interface*/
interface Gear{
    void handleGear();
}

/* Concrete Implementor - 1 */
class ManualGear implements Gear{
    public void handleGear(){
        System.out.println("Manual gear");
    }
}
/* Concrete Implementor - 2 */
class AutoGear implements Gear{
    public void handleGear(){
        System.out.println("Auto gear");
    }
}
/* Abstraction (abstract class) */
abstract class Vehicle {
    Gear gear;
    public Vehicle(Gear gear){
        this.gear = gear;
    }
    abstract void addGear();
}
/* RefinedAbstraction - 1*/
class Car extends Vehicle{
    public Car(Gear gear){
        super(gear);
        // initialize various other Car components to make the car
    }
    public void addGear(){
        System.out.print("Car handles ");
        gear.handleGear();
    }
}
/* RefinedAbstraction - 2 */
class Truck extends Vehicle{
    public Truck(Gear gear){
        super(gear);
        // initialize various other Truck components to make the car
    }
    public void addGear(){
        System.out.print("Truck handles " );
        gear.handleGear();
    }
}
/* Client program */
public class BridgeDemo {    
    public static void main(String args[]){
        Gear gear = new ManualGear();
        Vehicle vehicle = new Car(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Car(gear);
        vehicle.addGear();

        gear = new ManualGear();
        vehicle = new Truck(gear);
        vehicle.addGear();

        gear = new AutoGear();
        vehicle = new Truck(gear);
        vehicle.addGear();
    }
}

produzione:

Car handles Manual gear
Car handles Auto gear
Truck handles Manual gear
Truck handles Auto gear

Spiegazione:

  1. Vehicle è un'astrazione.
  2. Care Trucksono due implementazioni concrete di Vehicle.
  3. Vehicledefinisce un metodo astratto: addGear().
  4. Gear è l'interfaccia implementatore
  5. ManualGeare AutoGearsono due implementazioni di Gear
  6. Vehiclecontiene l' implementorinterfaccia anziché implementare l'interfaccia. Compositondell'interfaccia dell'attuatore è il punto cruciale di questo modello: consente all'astrazione e all'implementazione di variare in modo indipendente.
  7. Care Truckdefinire l'implementazione (astrazione ridefinita) per l'astrazione:: addGear()Contiene Gear- O ManualoppureAuto

Casi d'uso per il modello Bridge :

  1. Astrazione e implementazione possono cambiare indipendentemente tra loro e non sono vincolate al momento della compilazione
  2. Mappa gerarchie ortogonali: una per astrazione e una per implementazione .

"L'adattatore fa funzionare le cose dopo che sono state progettate; Bridge le fa funzionare prima che lo siano." Si consiglia di esaminare l'adattatore collegabile. È una variante di Adapter descritta da GoF nella sezione "Adapter" del loro libro Design Patterns. Lo scopo è creare un'interfaccia per le classi che non esistono ancora. Un adattatore collegabile non è un Bridge, quindi non credo che il primo punto sia valido.
c1moore,

Sebbene il cambio manuale e automatico possa richiedere implementazioni diverse per camion e auto
andigor

9

Ho usato il modello del ponte al lavoro. Programma in C ++, dove viene spesso chiamato linguaggio PIMPL (puntatore all'implementazione). Sembra così:

class A
{
public: 
  void foo()
  {
    pImpl->foo();
  }
private:
  Aimpl *pImpl;
};

class Aimpl
{
public:
  void foo();
  void bar();
};  

In questo esempio class Acontiene l'interfaccia e class Aimplcontiene l'implementazione.

Un uso per questo modello è quello di esporre solo alcuni membri pubblici della classe di implementazione, ma non altri. Solo nell'esempio Aimpl::foo()può essere chiamato tramite l'interfaccia pubblica di A, ma nonAimpl::bar()

Un altro vantaggio è che è possibile definire Aimplin un file di intestazione separato che non deve essere incluso dagli utenti di A. Tutto quello che devi fare è utilizzare una dichiarazione forward di Aimplprima che Asia definita e spostare le definizioni di tutte le funzioni membro che fanno riferimento pImplnel file .cpp. Questo ti dà la possibilità di mantenere l' Aimplintestazione privata e ridurre i tempi di compilazione.


2
Se usi questo modello, AImpl non ha nemmeno bisogno di un'intestazione. L'ho appena inserito nel file di implementazione per la classe A
1800 INFORMAZIONI,

Il tuo implementatore è privato. Ho una nuova domanda al riguardo, vedi stackoverflow.com/questions/17680762/…
Roland

7

Per inserire un esempio di forma nel codice:

#include<iostream>
#include<string>
#include<cstdlib>

using namespace std;

class IColor
{
public:
    virtual string Color() = 0;
};

class RedColor: public IColor
{
public:
    string Color()
    {
        return "of Red Color";
    }
};

class BlueColor: public IColor
{
public:
    string Color()
    {
        return "of Blue Color";
    }
};


class IShape
{
public:
virtual string Draw() = 0;
};

class Circle: public IShape
{
        IColor* impl;
    public:
        Circle(IColor *obj):impl(obj){}
        string Draw()
        {
            return "Drawn a Circle "+ impl->Color();
        }
};

class Square: public IShape
{
        IColor* impl;
    public:
        Square(IColor *obj):impl(obj){}
        string Draw()
        {
        return "Drawn a Square "+ impl->Color();;
        }
};

int main()
{
IColor* red = new RedColor();
IColor* blue = new BlueColor();

IShape* sq = new Square(red);
IShape* cr = new Circle(blue);

cout<<"\n"<<sq->Draw();
cout<<"\n"<<cr->Draw();

delete red;
delete blue;
return 1;
}

L'output è:

Drawn a Square of Red Color
Drawn a Circle of Blue Color

Nota la facilità con cui nuovi colori e forme possono essere aggiunti al sistema senza portare a un'esplosione di sottoclassi a causa di permutazioni.


0

per me lo considero un meccanismo in cui è possibile scambiare interfacce. Nel mondo reale potresti avere una classe che può utilizzare più di un'interfaccia, Bridge ti consente di scambiare.


0

Lavori per una compagnia assicurativa in cui sviluppi un'applicazione per il flusso di lavoro che gestisce diversi tipi di attività: contabilità, contratto, sinistri. Questa è l'astrazione. Dal punto di vista dell'implementazione, devi essere in grado di creare attività da diverse fonti: e-mail, fax, e-mail.

Inizi il tuo design con queste classi:

public class Task {...}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}

Ora, poiché ogni origine deve essere gestita in un modo specifico, decidi di specializzare ogni tipo di attività:

public class EmailAccountingTask : AccountingTask {...}
public class FaxAccountingTask : AccountingTask {...}
public class EmessagingAccountingTask : AccountingTask {...}

public class EmailContractTask : ContractTask {...}
public class FaxContractTask : ContractTask {...}
public class EmessagingContractTask : ContractTask {...}

public class EmailClaimTask : ClaimTask {...}
public class FaxClaimTask : ClaimTask {...}
public class EmessagingClaimTask : ClaimTask {...}

Si finisce con 13 lezioni. L'aggiunta di un tipo di attività o di un tipo di origine diventa problematica. L'uso del modello bridge produce qualcosa di più facile da mantenere disaccoppiando l'attività (l'astrazione) dalla fonte (che è una preoccupazione di implementazione):

// Source
public class Source {
   public string GetSender();
   public string GetMessage();
   public string GetContractReference();
   (...)
}

public class EmailSource : Source {...}
public class FaxSource : Source {...}
public class EmessagingSource : Source {...}

// Task
public class Task {
   public Task(Source source);
   (...)
}
public class AccountingTask : Task {...}
public class ContractTask : Task {...}
public class ClaimTask : Task {...}

L'aggiunta di un tipo di attività o di una fonte è ora molto più semplice.

Nota: la maggior parte degli sviluppatori non creerebbe la gerarchia di 13 classi in anticipo per gestire questo problema. Tuttavia, nella vita reale, potresti non conoscere in anticipo il numero di fonti e tipi di attività; se hai solo una fonte e due tipi di attività, probabilmente non disaccoppi l'attività dalla fonte. Quindi, la complessità complessiva aumenta man mano che vengono aggiunte nuove fonti e tipi di attività. Ad un certo punto, refatterai e, molto spesso, finirai con una soluzione a ponte.


-5
Bridge design pattern we can easily understand helping of service and dao layer.

Dao layer -> create common interface for dao layer ->
public interface Dao<T>{
void save(T t);
}
public class AccountDao<Account> implement Dao<Account>{
public void save(Account){
}
}
public LoginDao<Login> implement Dao<Login>{
public void save(Login){
}
}
Service Layer ->
1) interface
public interface BasicService<T>{
    void save(T t);
}
concrete  implementation of service -
Account service -
public class AccountService<Account> implement BasicService<Account>{
 private Dao<Account> accountDao;
 public AccountService(AccountDao dao){
   this.accountDao=dao;
   }
public void save(Account){
   accountDao.save(Account);
 }
}
login service- 
public class LoginService<Login> implement BasicService<Login>{
 private Dao<Login> loginDao;
 public AccountService(LoginDao dao){
   this.loginDao=dao;
   }
public void save(Login){
   loginDao.save(login);
 }
}

public class BridgePattenDemo{
public static void main(String[] str){
BasicService<Account> aService=new AccountService(new AccountDao<Account>());
Account ac=new Account();
aService.save(ac);
}
}
}

5
Ho annullato il voto perché ritengo che questa sia una risposta contorta, mal formattata.
Zimano,

1
Totalmente d'accordo, come puoi pubblicare le risposte su questo sito senza una minima attenzione al rientro del codice e alla chiarezza
Massimiliano Kraus,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.