Introduction to Bridge Design pattern( Java, c# example)

In this article we are going to learn about Bridge Design Pattern. What is Bridge pattern? What are the basic principles? How to implement and where we can use this.
I have used Java with Eclipse and C# with VS2010.

What is Bridge Design Pattern?
Bridge design pattern allows us to decouple abstraction from implementation. That means, bridge design pattern will allow a client to use any class with it's abstract class and subclass will be defined later on.
More simply, we can make program/function(from client) of one or more inter related abstract classes where specific sub lasses will have different implementation of those functions.It's little bit thinking of "why we need " as we can simply apply abstract super class to do that. But, what if more than one dependent abstract class containing those functions which will be implemented to their subclass. Or even think of an abstract class have other abstract classes as members.

Let's think a framework that gets data from database, may be SQL server, mySQL, Oracle or even a file system. So, if we have a gateway abstraction layer that is used by every call and specific implementation is managed by client , then it will be work like bridge.

Or, think of a bank. Where users come to create bank accounts. So, there are set of process done by bank with different type personals interacting to each other. And, for every type of account the process is different . So, if we use bridge pattern , then we get an abstract class to interact for account creation which will be specified to individual account type managers and client and associate objects. 
Bridge is mainly used when we want to make a group of class that adds functionality from one class to next.
You may find similarity with other patterns but do not get confused, see the example it will be simple and easy.

Basic Principle:
- Decouple abstraction from implementation.
- Individual implementation can have chance to vary from each other.
-Progressively adding functions while separating out major differences.

UML :


How to Implement?
Let's think of how to implement. As bridge pattern allows all communication via abstraction. So, we need interacting classes arranged into groups and each groups will have an abstract class.
Then, we may apply an interface representing the abstraction and implemented in those abstract classes responsibility wise. ( to keep simple, I have not use interface but you may see the interface in the example).
And, we may use composition for inter related objects by their abstract classes. (in the example, I use specific class as it was logical, but if you follow the comment there , you can use the abstraction for composition). So, in summary we will have
1. Abstraction : Defines in an interface / abstract class(in example, flasher)
2. Abstract Implementation: Apply abstraction with Implementor reference(interface)
3. Implementor : An interface defines implementations classes(from example, device)
4. ConcreteImplementor1, ConcreteImplementor2 : Implements the Implementor(from example, devices)

Example : I am going to use a very easy example. In real world , this example has a lot of implementation, I just use print console to understand bridge pattern only. Okay, let start. Assume we have some device, for USB and Serial device. We have to make a software that flash those two type of device. And our client will call a flasher (using abstraction) along with a device to flash that. In a single line client -> flasher [device]. Now, lets elaborate the procedure. (so that we can get logic behind bridge implementation)
To flash a device, the system has to go through some procedure.
1. Device need to be connected. Usually, a device provide a status of getting connected
2. Validate the device. Each type of device has his own validation method. So, USB and Serial devices have separate implementation.
3. Flashing initiation. Every flasher(who flashes usb/serial device) will initiate the device by sending initiation signal.
4. Communicate, each type of flasher will communicate with its compatible device for flashing permission(on to go) using its own protocol (USB/Serial). That means, USB flasher will communicate an usb device for flashing final permission.(same for serial)
5. Flash device, like as communication, each type of flasher will flash its associate type device.
6. Eject device, after every successful flashing all flasher will eject the device.

Now, lets distribute the roles, A device will
1. Communicate
2. Validate
3. Eject

And among them, 2. validate is separate for device type. So, we have to make it implementing among sub class.  And, A flasher will 
1. Initiate flashing
2. Communicate via protocall
3. Flash device
And, among them , 2 & 3 are separate for flasher type. That means, those two will be implemented among sub classes.

And the client will simply call a flasher via Abstract class and initiate a specific flasher to related device. But, client will do same procedure for both of them.

So, lets start with Device abstract class. 
public abstract class Device {
protected String type="Generic Device";
protected boolean deviceConnectionStatus=false;
public String getType() {
return type;
}
public void connectDevice(){
System.out.println("A device is connected"); 
deviceConnectionStatus=true;
}
public abstract boolean validateDevice();
public void ejectDevice(){
System.out.println("Ejecting.....");
deviceConnectionStatus=false;
} 
}
And, implementing USB & Serial devices are
public class SerialDevice extends Device{
    public SerialDevice(){
        type="Serial";
        deviceConnectionStatus=true;
    }
    public boolean validateDevice() {
        System.out.println("System is validating Serial device");
        return true;
    }
}
public class USBDevice extends Device{
    public USBDevice(){
        type="USB";
        deviceConnectionStatus=true;
    }
    public boolean validateDevice() {
        System.out.println("System is validating USB Device");
        return true;
    }
}

Now, lets apply abstract flasher class.
public abstract class Flasher {
    protected Device myDevice = null;
    public boolean flashStatus=false;
    public Flasher(Device aDevice){
        myDevice=aDevice;
    }
    public void connectDevice(){
        myDevice.connectDevice();
    }
    public void validateDevice(){
        if(myDevice.validateDevice()){
            System.out.println(myDevice.getType()+" device is a valid in the system");
            flashStatus=true;
        }else{
            System.out.println(myDevice.getType()+" device is a not valid in the system");
        }
    }
    public void initFlashing(){
        System.out.println("A "+myDevice.getType() +" is initilizing from flashing");
    }
    public abstract void communicateViaProtocall();
    public abstract void flashDevice();
    public void eject(){
        myDevice.ejectDevice();
    }
}
And implementing USB & Serial flashers
public class SerialFlasher extends Flasher{
public SerialFlasher(SerialDevice aDevice) 
//we can keep that abstract(that means public SerialFlasher(Device aDevice)) but, 
//i felt it is logical to use specific flasher for specific device. 
    {
        super(aDevice);
    }
    public void communicateViaProtocall() {
        System.out.println("Sysem is communicating with USB devices for flashing");
    }
    public void flashDevice() {
        System.out.println("USB Flasher is flashing "+myDevice.getType()+" Device");
    }
}
public class USBFlasher extends Flasher{
public USBFlasher(USBDevice aDevice) 
//we can keep that abstract(that means public USBFlasher(Device aDevice))
// but, i felt, it is logical to use specific flasher for specific device. 
    {
        super(aDevice);
    }
    public void communicateViaProtocall() {
        System.out.println("Sysem is communicating with serial port for flashing");
    }
    public void flashDevice() {
        System.out.println("Serial Flasher is flashing "+myDevice.getType()+" Device");
    }
}

So, now the client program which contains main and calls the flasher to flash a specific device

public class Program {
    public static void main(String[] args) {
        Flasher anUsbFlasher = new USBFlasher(new USBDevice());
        Flasher aSerialFlasher = new SerialFlasher(new SerialDevice());        
       System.out.println("we are flashing serial device");
        aSerialFlasher.connectDevice();
        aSerialFlasher.validateDevice();
        aSerialFlasher.initFlashing();
        aSerialFlasher.communicateViaProtocall();
        if(aSerialFlasher.flashStatus){
            aSerialFlasher.flashDevice();
        }
        aSerialFlasher.eject();        
        System.out.println("\nwe are flashing usb device");
        anUsbFlasher.connectDevice();
        anUsbFlasher.validateDevice();
        anUsbFlasher.initFlashing();
        anUsbFlasher.communicateViaProtocall();
        if(anUsbFlasher.flashStatus){
            anUsbFlasher.flashDevice();
        }
        anUsbFlasher.eject();
    }
}
Java Example :
1. You will see interface for device and flasher, but I have not used that. You may us in abstract classes. 
2. Some extra console print are there to understand clearly.

C# Example :
1. I have change some method names as dot net works differently
2. I have change some get method to property

Uses :
1. Very good for GUI framework development where GUI properties/ functionality/events can be added separately but abstract implementation are there.
2. Persistence framework building(used in API)
3. Test framework building in selenium for data driven/ keyword driven where the main functionality will be abstracted but page objects will be implemented page specific in separate classes.

Thanks...:) 

No comments:

Post a Comment