javadesign-patternsabstractionbridge

Does the Bridge Pattern decouples an abstraction from implementation?


I learned Bridge pattern from different articles and I have implemented that as per my understanding . One thing that is confusing me is bridge pattern says

BridgePattern decouples an abstraction from its implementation so that the two can vary independently

what is meaning of this statement? Is implementation resides at in separate jar ?

what is meaning of vary independently statement ?

considering the provided journaldev article, elaborate the answer.

Any help is greatly appreciated.


Solution

  • BridgePattern decouples an abstraction from its implementation.

    Abstraction and Implementation can vary independently since the concrete class does not directly implement Abstraction ( interface)

    Implementation never refers Abstraction. Abstraction contains Implementation interface as a member (through composition).

    Coming back to your question regarding the example code in [journaldev][4] article :

    Shape is Abstraction

    Triangle is RedefinedAbstraction

    Color is Implementor

    RedColor is ConcreteImplementor

    A concrete Shape object : Triangle extends Shape but does not implement the Color interface.

    public class Triangle extends Shape{
    }
    

    RedColor and GreenColor actually implement the Color interface.

    The Concrete Shape object (Triangle) is independent of implementing abstraction (i.e. Color interface).

    Shape tri = new Triangle(new RedColor());
    

    Here Triangle contains a concrete Color object ( Composition). If there is a change in the Color abstraction (interface), RedColor and GreenColor are responsible for implementing the abstraction of Color interface.

    Shapes like Triangle is not affected by changes in contract to the Color interface. So the Color interface can vary independently. This is possible because Shape holds the contract that uses Composition rather than implementation.

    Example code to understand the pattern better:

    Example code:

    /* Implementor interface*/
    interface Gear{
        void handleGear();
    }
    
    class VehicleAttributes{
        String name;
        String engineID;
        String chasisNumber;
        int seats;
        int engineCapacity;
        double height;
        double length;
    }
    
    /* 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;
        /* Mutable state of Vehicle */
        VehicleAttributes attributes;
        public Vehicle(Gear gear){
            this.gear = gear;
        }
        abstract void addGear();
        public void setVehicleAttributes(VehicleAttributes attributes){
            this.attributes = attributes;
        }
        
    }
    /* 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();
            /* Create specific properties of Car in attributes */
            //vehicle.setVehicleAttributes(attributes);
            
            gear = new ManualGear();
            vehicle = new Truck(gear);
            vehicle.addGear();
            /* Create specific properties of Truck in attributes */
            //vehicle.setVehicleAttributes(attributes);
            
            gear = new AutoGear();
            vehicle = new Truck(gear);
            vehicle.addGear();
            /* Create specific properties of Truck in attributes */
            //vehicle.setVehicleAttributes(attributes);
        }
    }
    

    output:

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

    Explanation:

    1. Vehicle is an abstraction.
    2. Car and Truck are two concrete implementations of Vehicle.
    3. Vehicle defines an abstract method : addGear().
    4. Gear is implementor interface
    5. ManualGear and AutoGear are two implementations of Gear
    6. Vehicle contains implementor interface rather than implementing the interface. Compositon of implementor interface is crux of this pattern : It allows abstraction and implementation to vary independently.
    7. Car and Truck define implementation ( redefined abstraction) for abstraction : addGear() : It contains Gear - Either Manual or Auto