Wednesday, 12 March 2008

Bridge Design Pattern in Java

Today we will be discussing another GoF Structural Pattern "Bridge".

Overview:
An abstraction and implementation are in different class hierarchies.

Intent:
1) Bridge Pattern decouples an abstraction from its implementation so that two can vary independently.This is unlike the of the AdapterPattern which exists only to adopt the interface of one class to another.
2) Publish interface in an inheritance hierarchy,and bury implementation in its own inheritance hierarchy.
3) Beyond Encapsulation,to insulation.

Bridge Pattern is commonly known as "Driver". E.g Printer Driver,Graphics Driver,Sound Driver,etc.

Adapter is used to adopt two different classes to operate in a unified way.The adopter is the solution for classes that do similar jobs but don't have a unified interface.The major diff between adapter and bridge is when they are used.Adapter is used when two incompatible interfaces have to be unified together,i.e adapter is a result of existing incompatabilities.But bridge is used when we actually need to seperate interface from implementation.Varying types of implementation is one reason
- Praveen.

Bridge might be a situation where the programmer thought it would be best to isolate the handling of the system-dependent stuff from the handling of the system-independent stuff.

Related Patterns:
An abstract factory can create and configure a particular Bridge.

Adapter Pattern is geared toward making unrelated classes work together.It is usually applied to the systems after they have been designed.Bridge on the other hand is used up-front in a design to let abstractions and implementations vary independently.

Bridge is a synonym for the "handle/body" idiom [Coplien, C++ Report, May 95, p58]. This is a design mechanism that encapsulates an implementation class inside of an interface class. The former is the body, and the latter is the handle. The handle is viewed by the user as the actual class, but the work is done in the body. "The handle/body class idiom may be used to decompose a complex abstraction into smaller, more manageable classes. The idiom may reflect the sharing of a single resource by multiple classes that control access to it (e.g. reference counting)." [Coplien, Advanced C++, p62]

Bridge emphasizes identifying and decoupling "interface" abstraction from "implementation" abstraction.

The Bridge pattern decouples an abstraction from its implementation, so that the two can vary independently. A household switch controlling lights, ceiling fans, etc. is an example of the Bridge. The purpose of the switch is to turn a device on or off. The actual switch can be implemented as a pull chain, simple two position switch, or a variety of dimmer switches. [Michael Duell, "Non-software examples of software design patterns", Object Magazine, Jul 97, p54]

State, Strategy, Bridge (and to some degree Adapter) have similar solution structures. They all share elements of the "handle/body" idiom [Coplien, Advanced C++, p58]. They differ in intent - that is, they solve different problems.

The structure of State and Bridge are identical (except that Bridge admits hierarchies of envelope classes, whereas State allows only one). The two patterns use the same structure to solve different problems: State allows an object's behavior to change along with its state, while Bridge's intent is to decouple an abstraction from its implementation so that the two can vary independently. [Coplien, C++ Report, May 95, p58]

The JDBC architecture decouples an abstraction from its implementation so that the two can vary independently—an excellent example of Bridge.

Implementation Issues:

1) Only One Implementor.
In situations where there's only one implementation,creating an abstract implementor class isn't necessary.This is a degenerate case of the bridge pattern; there's a one-to-one relationship between Abstraction and Implementor.Nevertheless,this seperation is still useful when a change in the implementation of a class must not affect its existing clients- that is they shouldn't have to be recompiled,just relinked.

2) Creating the right Implementor object.
How ,when, and where do you decide which implementor class to instantiate when there's more than once?

If Abstraction knows about all ConcreteImplementor classes, then it can instantiate one of them in its constructor; it can decide between them based on parameters passed to its constructor. If, for example, a collection class supports multiple implementations, the decision can be based on the size of the collection. A linked list implementation can be used for small collections and a hash table for larger ones.
Another approach is to choose a default implementation initially and change it later according to usgae.For example, if the collection grows bigger than a certain threshold, then it switches its implementation to one that's more appropriate for a large number of items.

3) We cannot implement a Bridge using multiple Inheritance.

Consequences of the Bridge Pattern.

1. Decoupling interface and implementation. An implementation is not bound permanently to an interface.
The implementation of an abstraction can be configured at run-time. It's even possible for an object to change its implementation at run-time.

Decoupling Abstraction and Implementor also eliminates compile-time dependencies on the implementation.Changing an implementation class doesnot require recompiling the Abstract class and its clients.This property is essential when you must ensure binary compatibility between different versions of a class library.Furthermore, this decoupling encourages layering that can lead to a better-structured system. The high level part of a system only has to know about Abstraction and Implementor.

2. Improved Extensibility:
You can extend the abstraction and implementor hierarchies independently.

3. Hiding Implementation Details from Clients.
You can shield clients from implementation details, like the
sharing of implementor objects and the accompanying reference count mechanism (if any).

Below is one example of the implementation of Bridge Pattern.


package patterns;

abstract class Soda {
SodaImp sodaImp;

public SodaImp getSodaImp() {
return sodaImp;
}

public void setSodaImp() {
this.sodaImp = SodaImpSingleton.getTheSodaImp();
}

public abstract void pourSoda();
}

class SodaImpSingleton {
private static SodaImp sodaImp;

public SodaImpSingleton(SodaImp sodaImpIn) {
this.sodaImp = sodaImpIn;
}

public static SodaImp getTheSodaImp() {
return sodaImp;
}

}

abstract class SodaImp {
public abstract void pourSodaImp();
}

class CherrySodaImp extends SodaImp {
CherrySodaImp() {}
public void pourSodaImp() {
System.out.println("Cheery Soda");
}
}

class OrangeSodaImp extends SodaImp {
OrangeSodaImp() {}
public void pourSodaImp() {
System.out.println("Orange Soda");
}
}

class GrapeSodaImp extends SodaImp {
GrapeSodaImp() {}
public void pourSodaImp() {
System.out.println("Cheery Soda");
}
}

class MediumSoda extends Soda {

public MediumSoda() {
setSodaImp();
}

public void pourSoda() {
SodaImp sodaImp=this.getSodaImp();
for(int i=0;i<2;i++) {
sodaImp.pourSodaImp();
}
}

}

public class BridgePatternExample {
public static void main(String[] args) {
SodaImpSingleton sodaImpSingleton =new SodaImpSingleton(new CherrySodaImp());
MediumSoda mediumSoda=new MediumSoda();
mediumSoda.pourSoda();
}
}




Hope this explanation helps if any.

No comments: