Monday, November 30, 2015

The Bridge design pattern

This tutorial presents the Bridge design pattern. Bridge is a structural pattern and allows the decoupling of an abstract class from its implementations. The pattern dictates the use of an interface between components, which  acts as a bridge and unbounds the abstraction from the implementation at compile time. This way both abstraction and implementations are independently extensible. Any possible changes in the implementation of the abstractions have no impact on the clients and in fact the implementation details are hidden from them (i.e., the clients).

To showcase the bridge pattern in a tutorial, let's consider electronic devices and remote controllers for handling such devices.  We have an abstraction representing the electronic devices and remote controllers. We use a bridge remote control interface between the electronic devices and the implementations of the remote controls. The UML class diagram is depicted in the following figure:



We start by the interface which all electronic devices implement and defines just on() and off() methods:
package com.tasosmartidis.design_patterns_tutorial.bridge;

public interface ElectronicDevice {

    public void on();
    
    public void off();  
}

We consider in our scenario two electronic devices, a TV and a radio. The Television class implements the ElectronicDevice inteface:
package com.tasosmartidis.design_patterns_tutorial.bridge;

public class Television implements ElectronicDevice {

    private boolean isOn = false;
    
    @Override
    public void on() {
        if(isOn)
            System.out.println("TV is already on, no action required!");
        else {
            System.out.println("Turning on TV...");
            isOn = true;
        }       
    }

    @Override
    public void off() {
        if(isOn) {
            System.out.println("Turning off TV...");
            isOn = false;
        }
        else
            System.out.println("TV is already off, no action required!");       
    }
}

And the Radio class does the same:
package com.tasosmartidis.design_patterns_tutorial.bridge;

public class Radio implements ElectronicDevice {

    private boolean isOn = false;
    
    @Override
    public void on() {
        if(isOn)
            System.out.println("Radio is already on, no action required!");
        else {
            System.out.println("Turning on radio...");
            isOn = true;
        }       
    }

    @Override
    public void off() {
        if(isOn) {
            System.out.println("Turning off radio...");
            isOn = false;
        }
        else
            System.out.println("Radio is already off, no action required!");        
    }

}

Now, we have our bridge abstract class. Electronic devices need remotes to be handled from far away. We consider that all remotes are providing three buttons,. Two of them are used for use of the on() and off() operations on electronic devices and a wild card button is left to the implementation of each remote controller to define its function. The ElectronicDevice class remote looks like this:
package com.tasosmartidis.design_patterns_tutorial.bridge;


public abstract class ElectronicDeviceRemote {

    private ElectronicDevice device;
    
    public ElectronicDeviceRemote(ElectronicDevice device) {
        this.device = device;
    }
    
    public void on() {
        this.device.on();
    }
    
    public void off(){
        this.device.off();
    }
    
    public abstract void wildCardDeviceButton(); 
}

Next, we will create two remotes, a cheap and an expensive one. We start with the cheap remote which doesn't really make use of the wild card button:
package com.tasosmartidis.design_patterns_tutorial.bridge;

public class CheapRemote extends ElectronicDeviceRemote {

    public CheapRemote(ElectronicDevice device) {
        super(device); 
    }
    
    public void wildCardDeviceButton() {
        System.out.println("Too cheap to use the button!");
    }
}

The expensive remote, uses the button to provide timer for turning off the electronic device:
ackage com.tasosmartidis.design_patterns_tutorial.bridge;


public class ExpensiveRemote extends ElectronicDeviceRemote {

    public ExpensiveRemote(ElectronicDevice device) {
        super(device); 
    }
    
    public void wildCardDeviceButton() {
        System.out.println("Automated swich-off of device in 5'");
        try {
            // Let's not wait for the whole 5'
            Thread.sleep(3000);
        } catch (InterruptedException e) { 
            e.printStackTrace();
        }
        super.off();
    }
}


Finally, let's see how our devices and remotes work in a demo application:
package com.tasosmartidis.design_patterns_tutorial.bridge;


public class BridgeDemo {

    public static void main(String[] args) {
        ElectronicDevice tv = new Television(); 
        ElectronicDevice radio = new Radio(); 
        
        ElectronicDeviceRemote expensiveRemote = new ExpensiveRemote(tv);
        ElectronicDeviceRemote anotherExpensiveRemote = new ExpensiveRemote(radio);
        ElectronicDeviceRemote cheapRemote = new CheapRemote(tv);
        
        System.out.println("TV with the expensive remote!");
        expensiveRemote.on();
        expensiveRemote.on();
        expensiveRemote.wildCardDeviceButton();
        expensiveRemote.off();
        System.out.println();
        
        System.out.println("TV with the cheap remote!");
        cheapRemote.on();
        cheapRemote.wildCardDeviceButton();
        cheapRemote.off();
        cheapRemote.off();
        System.out.println();
        
        System.out.println("Radio with the expensive remote!");
        anotherExpensiveRemote.on();
        anotherExpensiveRemote.wildCardDeviceButton();
        anotherExpensiveRemote.off();
        System.out.println();

    }
}

And running the application produces the following results:

The application produced the expected results and out bridge tutorial is completed. The project is available on GitHub.

No comments:

Post a Comment