Difference between Bridge pattern and Adapter pattern

Viewed 70187

What is the difference between the Bridge and Adapter patterns?

11 Answers

In the top answer, @James quotes a sentence from the GoF, page 219. I think it's worthwhile reproducing the full explanation here.

Adapter versus Bridge

The Adapter and Bridge patterns have some common attributes. Both promote flexibility by providing a level of indirection to another object. Both involve forwarding requests to this object from an interface other than its own.

The key difference between these patterns lies in their intents. Adapter focuses on resolving incompatibilities between two existing interfaces. It doesn't focus on how those interfaces are implemented, nor does it consider how they might evolve independently. It's a way of making two independently designed classes work together without reimplementing one or the other. Bridge, on the other hand, bridges an abstraction and its (potentially numerous) implementations. It provides a stable interface to clients even as it lets you vary the classes that implement it. It also accommodates new implementations as the system evolves.

As a result of these differences, Adapter and Bridge are often used at different points in the software lifecycle. An adapter often becomes necessary when you discover that two incompatible classes should work together, generally to avoid replicating code. The coupling is unforeseen. In contrast, the user of a bridge understands up-front that an abstraction must have several implementations, and both may evolve independently. The Adapter pattern makes things work after they're designed; Bridge makes them work before they are. That doesn't mean Adapter is somehow inferior to Bridge; each pattern merely addresses a different problem.

Looks like shorter and clear answer to me according to another stackoverflow answer here:

  • Adapter is used when you have an abstract interface, and you want to map that interface to another object which has similar functional role, but a different interface.

  • Bridge is very similar to Adapter, but we call it Bridge when you define both the abstract interface and the underlying implementation. I.e. you're not adapting to some legacy or third-party code, you're the designer of all the code but you need to be able to swap out different implementations.

Bridge is very similar to Adapter, but we call it Bridge when you define both the abstract interface and the underlying implementation, means not adapting to external code, you're the designer of all the code but you need to be able to swap out different implementations.

There are plenty of answers To distinguish between Adapter and Bridge. But as ppl are looking for code examples, I would give one example for Adapter Design Pattern crafted into a timeline story :

    //---------------------------------------External Vendor/Provider--------------------------------
   
            //Adaptee | RussianTankInterface is adaptee | adaptee lives in is own lala land and do not care about any other class or interface
            RussianTankInterface smerch9K58 = new RussianTank("The Russian Artillery bought by India in October 2015");
                smerch9K58.aboutMyself();
                smerch9K58.stuff();
                smerch9K58.rotate();
                smerch9K58.launch();
        
   
    //---------------------------------2016 : India manufactures Bharat52 ------------------------------ 
    
            //Client_1 :: IndianTank 
            EnemyAttacker bharat52Attacker = new IndianTank("Tank built in India delivered to Army in Jul 2016"); 
   
            // behaves normally -------------------------(1)
                bharat52Attacker.aboutMe();
                bharat52Attacker.load();
                bharat52Attacker.revolve();
                bharat52Attacker.fireArtillery();
                
    //---------------------------------2019 : India mnufactures Pinaka, and thought about fusion with Russian technology - so adaption required ------------------------------ 
                  
            //Client_2 :: IndianTank 
            EnemyAttacker pinakaAttacker = new IndianTank("Tank built in India in 1998 got upgraded_v1 in 9 Sep 2019"); 
            
            
            
#####----Bilateral-Coalition happens----## 
#####       India  : I want a fusion artillery technology with 
#####                       1) Indian materials and brain-power but
#####                       2) Russian machine-parts-movement technology 
#####       Russia : Give me your Interface - at max we can help by providing an Adapter 
    
    //---------------------------------------External Vendor/Provider-----------------------------------
    
            //Adapter :: RussianTechnologyAdapter | Russia gets EnemyAttacker interface only from India & creates RussianTechnologyAdapter 
            RussianTechnologyAdapter russianTechnologyAdapter = new RussianTechnologyAdapter(smerch9K58);
           
           
            //Target | EnemyAttacker was initially ClientInterface but later becomes the Target as story evolves | <- client owns this Interface
            EnemyAttacker dhanushAttacker = russianTechnologyAdapter;
            
#####----Russia keeps her Word----## 
#####       Russia to India : Here you go! Take Dhanush, a wrapper over our encapsulated adapter, and plug-in anything conforming to your EnemyAttacker.
#####       India : Thanks a lot!

    //--------------------------------- 2020 : India returns back happily with dhanushAttacker--------------------------------------- 
            
            //Client_2 - adapted behavior -------------------------(2)
                dhanushAttacker.setNavigationCapability(pinakaAttacker.getCuttingEdgeNavigableTargets());
                dhanushAttacker.aboutMe(); //calls RussianInstance -> aboutMyself()
                dhanushAttacker.load();    //calls RussianInstance -> stuff()
                dhanushAttacker.revolve(); //calls RussianInstance -> rotate()
                dhanushAttacker.fireArtillery();  //calls RussianInstance -> launch()
                       
          

Carefully notice :

  • Same API in (1) & (2) behaves differently
  • India can push its brain into wireFrame of Russia e.g dhanushAttacker.setNavigationCapability(pinakaAttacker.get(..))

Noteworthy points

Client side owns:

-  Invoker /Use(uses Adapter later point)/Client
-  ClientInterface (a.k.a Target )

Shared later:

- ClientInterface  ( becomes Target after sharing)

Receiver side owns :

- Adapter (later shared directly or as a wrapper )  
- Adaptee

     

Hoping someone gives an inline for Bridge too :)

I think it's simple.

The adapter is designed to allow a third party application to work with your application. Conversely, so that your application can work with third party applications.

Using the bridge pattern, it is supposed to connect two or more applications without implementing an adapter.

In fact, a bridge is an interface through which two applications will interact. As an example of bridging, these are PSR interfaces in PHP.

Example:

OtherApp

<?php

interface IRequestDataOtherApp {};
 
interface IResponseDataOtherApp {};
 
class ResponseDataOtherApp implements IResponseDataOtherApp   {

}; 
 
class OtherApp {
    public static function request(IRequestDataOtherApp $requestData):IResponseOtherApp
    {
        // code
        return new ResponseDataOtherApp ();
    }
}

MyApp

<?php 

interface IResponseDataMyApp {};

interface IReqestDataMyApp {};

class ReqestDataMyApp implements IReqestDataMyApp {};

class Adapter {
   
    public static function convertResponseData(IResponseDataOtherApp $response):IResponseDataMyApp 
    {
      
    }
    public static function convertRequestData(IReqestDataMyApp $request):IRequestOtherApp
    {
      
    }
};
$unformattedResponse=OtherApp::request(Adapter::convertRequestData(new ReqestDataMyApp ()));
$myResponse=ResponseAdapter::convertResponseData($unformattedResponse);
//...

In the before example, we have implemented 2 adapters per request and per response. If we rewrite example and try to implement the bridge.

<?php
    interface IBridgeResponse {};

OtherApp

<?php
interface IRequestDataOtherApp {};
interface IResponseDataOtherApp {};
 
class ResponseDataOtherApp  implements IBridgeResponse, IResponseDataOtherApp   {

}; 
 
class OtherApp {
    public static function request(IRequestDataOtherApp $requestData):IResponseOtherApp
    {
        // code
        return new ResponseDataOtherApp  ();
    }
}

MyApp

<?php 

interface IResponseDataMyApp {};

interface IReqestDataMyApp {};

class ReqestDataMyApp implements IReqestDataMyApp {};

class Adapter {

    public static function convertResponseData(IResponseDataOtherApp $response):IResponseDataMyApp 
    {
      
    }

    public static function convertRequestData(IReqestDataMyApp $request):IRequestOtherApp
   {
      
   }
};

$response=OtherApp::request(Adapter::convertRequestData(new ReqestDataMyApp ()));

if($response instanceof IBridgeResponse){
  /** 
The component has implemented IBridgeResponse interface,
thanks to which our application can interact with it.
This is the bridge.

Our application does not have to create an additional adapter
 (if our application can work with IBridgeResponse).
*/
}
//...

In hexagonal architecture, Ports (Interfaces, Contracts) can act as 'Bridges' if you write an application from the very beginning and are ready to accept the rules of another application being used. Otherwise, you will have to write 'Adapters'.

Related