Friday, May 20, 2011

Bridge Pattern

The Bridge Pattern allows you to vary the implementation and abstraction by placing the two in seperate hierarchies.
Decouple an abstraction or interface from its implementation so that the two can vary independently. It is a structural design pattern.
The bridge uses encapsulation, aggregation, and can use inheritance to separate responsibilities into different classes.

Example

The bridge pattern can be demonstrated with an example. Suppose we have a Vehicle class. We can extract out the implementation of the engine into an Engine class. We can reference this Engine implementor in our Vehicle via an Engine field. We'll declare Vehicle to be an abstract class. Subclasses of Vehicle need to implement the drive() method. Notice that the Engine reference can be changed via the setEngine() method.

Example Code for Bridge Pattern

Consider a class called Vehicle:

public abstract class Vehicle {
//Vehicle have components like engine, tyre
//add other components like headlight, music system etc..
//but I just took 2 for simplicity
Engine engine;
Tyre tyre;

//assembling cose is fixed here, but can be variable
float assemblingCost;

public abstract void drive();

public void setEngine(Engine engine) {
this.engine = engine;
}

public void setTyres(Tyre tyre)
{
this.tyre = tyre;
}

public void assembleVehicle(Engine engine,Tyre tyre)
{
this.engine=engine;
this.tyre = tyre;
}
public float getPrice()
{
return tyre.getCost()+engine.getCost()+assemblingCost;
}
}

 

Extending this base class – take BigTruck and SmallCar

public class BigTruck extends Vehicle {

public BigTruck(Engine engine,Tyre tyre) {
this.assemblingCost = 3000;
assembleEngine(engine,tyre);//calls assembleEngine
// from base class
}

@Override
public void drive() {
System.out.println("Driving the truck now, with cost:"+
getPrice();
}

}


public class SmallCar extends Vehicle {

public SmallCar(Engine engine,Tyre tyre) {
this.assemblingCost = 2000;
assembleEngine(engine,tyre);//calls assembleEngine
// from base class
}

@Override
public void drive() {
System.out.println("Driving the car now, with cost:"+
getPrice();

engine.kickStart();

tyre.roll();
}

}



Now getting the components of the cars:


Engine of the car:


public interface Engine {

public int kickStart();

}


BigEngine:

public class BigEngine implements Engine {

int horsepower;

public BigEngine() {
horsepower = 350;
}

@Override
public int kickStart() {
System.out.println("The big engine is running");
return horsepower;
}

}


Small Engine:

public class SmallEngine implements Engine {

int horsepower;

public SmallEngine() {
horsepower = 100;
}

@Override
public int kickStart() {
System.out.println("The small engine is running");
return horsepower;
}

}


Now get the tyres ready:

public interface Tyre{
public void roll();
}


Thick tyres:

public class ThickTyre implements Tyre{
private int width;
public ThickTyre(){
width=200;
}
public void roll(){
System.out.println("Rolling on thick tyres of width:"
+ width);
}
}


Medium Tyre:

public class MediumTyre implements Tyre{
private int width;
public MediumTyre (){
width=140;
}
public void roll(){
System.out.println("Rolling on thin tyres of width:"
+ width);
}
}


Now lets see the bridge demo, BridgeDemo:


public class BridgeDemo {

public static void main(String[] args) {

Vehicle vehicle = new BigTruck(new SmallEngine(),new ThickTyre());
vehicle.drive();
vehicle.assembleVehicle(new BigEngine(),new ThickTyre());
vehicle.drive();

vehicle = new SmallCar(new SmallEngine(),new MediumTyre());
vehicle.drive();
vehicle.assembleVehicle(new BigEngine(),new MediumTyre());
vehicle.drive();

}

}

Clearly we don't have to worry about assembling. Just name the part like big engine, we just provide and class takes it and do it. So Bridge Pattern provides such a level of abstraction. In addition, since BigTruck and SmallCar were both subclasses of the Vehicle abstraction, we were even able to point the vehicle reference to a BigTruck object and a SmallCar object and call the same drive() method for both types of vehicles.

Benefits of Bridge Pattern


  • Decoupling abstraction from implementation - Inheritance tightly couples an abstraction with an implementation at compile time. Bridge pattern can be used to avoid the binding between abstraction and implementation and to select the implementation at run time.
  • Reduction in the number of sub classes - Sometimes, using pure inheritance will increase the number of sub classes.
  • Cleaner code and Reduction in executable size. This results in a cleaner code without much preprocessor statements like #ifdefs, #ifndefs. Also, it is easy to conditionally compile CImageImp sub classes for a given operating system to reduce the size of the executable.
  • Interface and implementation can be varied independently - Maintaining two different class hierarchies for interface and implementation entitles to vary one independent of the other.
  • Improved Extensibility - Abstraction and implementation can be extended independently. As mentioned earlier, the above example can easily be extended to view other image formats on Windows or view BMP images on other operating systems.
  • Loosely coupled client code - Abstraction separates the client code from the implementation. So, the implementation can be changed without affecting the client code and the client code need not be compiled when the implementation changes.
  • No comments:

    Post a Comment

    Chitika