How to implement Bridge pattern in Java?

Photo by Jerome Dominici on Pexels.com

Let’s say you are a technical manager.

You have several projects under you.

You have designed them already to use different client and server technologies.

Client technologies include Angular , VueJS and React.

Server technologies include Java, DotNet and NodeJS.

You are combining these two categories in different combinations and using each combination for a particular project.

Eg) A project developed with Angular for the front end and Java at the backend.

Now you want programmers.

They need to implement both UI and service code.

Let’s represent this in Java.

Your final client code using Bridge pattern looks like this :

package structural.bridge.pattern;

public class Client {

	public static void main(String a[]) {
		
		Programmer programmer = new AngularProgrammer(new JavaProgrammer());

		programmer.developApplication();
		
		
		Programmer nextProgrammer = new ReactProgrammer(new DotNetProgrammer());
		
		nextProgrammer.developApplication();
		
		
		Programmer thirdProgrammer = new VueJSProgrammer(new NodeJSProgrammer());
		
		thirdProgrammer.developApplication();
		
		
		Programmer fourthProgrammer = new AngularProgrammer(new NodeJSProgrammer());
		
		fourthProgrammer.developApplication();
	}

}

The backend programmer instance is passed as a parameter inside the constructor of the front end programmer.

Instead if we had to represent each combination using subclasses , the number of subclasses would have been huge.

For example , to represent a programmer who uses Angular at the front end and Java at the backend we could have created a subclass named ‘AngularJavaProgrammer’ , make it extend the Programmer abstract class and then called on the developApplication() method.

Something like this :

Programmer angularJavaProgrammer = new AngularJavaProgrammer();
angularJavaProgrammer.developApplication();

Then for each combination you need to create a subclass : AngularDotNetProgrammer, AngularNodeJsProgrammer , ReactJavaProgrammer, ReactDotNetProgrammer, ReactNodeJSProgrammer and so on .

So many classes!

This can be avoided using Bridge Pattern.

Lets decouple the implementation of backend coding from the interface of Programmer.

Lets create a new interface named BackendProgrammer to handle backend implementation.

All the different backend implementations will be handled by inheriting this interface.

And then we pass an instance of this implementation to the Programmer classes which then delegate the backend work to the passed instance.

That’s it.

Let’s look at the abstract base class Programmer:

package structural.bridge.pattern;

public abstract class Programmer {
	
	BackendProgrammer backend;
	
	Programmer(BackendProgrammer backend){
		
		
		this.backend = backend;
	}

	abstract void writeUICode();
	
	 void writeServiceCode() {
		
		this.backend.writeService();
	}
	
	public void developApplication() {
		
		System.out.println("Application Development Starts");
		
		writeUICode();
		writeServiceCode();
		
		System.out.println();
	}
}

As you see one of the implementations (writeServiceCode()) is decoupled from this interface by delegating it to an instance from another class hierarchy which handles the backend implementation.

Let’s see one of the Programmer classes:

package structural.bridge.pattern;

public class AngularProgrammer extends Programmer {

	AngularProgrammer(BackendProgrammer backend) {
		super(backend);

	}

	@Override
	public void writeUICode() {

		System.out.println("Writing UI code in Angular");

	}

	
}

Similar classes are created for React and VueJS programmers.

Let’s see one of the classes for Backend implementation:

package structural.bridge.pattern;

public class JavaProgrammer implements BackendProgrammer {

	@Override
	public void writeService() {
		
		System.out.println("Writing Service in Java");

	}

}

Similar classes are created for other backend technologies NodeJS and DotNet.

Here is the backend programmer interface:

package structural.bridge.pattern;

public interface BackendProgrammer {

	
	void writeService();
	
}

Here is the output of the client code:

Application Development Starts
Writing UI code in Angular
Writing Service in Java

Application Development Starts
Writing UI code in React JS
Writing serivce in Dot Net

Application Development Starts
Write UI code in Vue JS
Writing service in NodeJS

Application Development Starts
Writing UI code in Angular
Writing service in NodeJS


A much neater design achieved through Bridge patterns.

All this does is :

decouple abstraction from its implementation (backend abstraction is present in Programmer base class but its implementation is not present in any of its subclasses , instead it is delegated to a seperate class hierarchy)

The code can be further optimized by replacing the below line in client code :

Programmer programmer = new AngularProgrammer(new JavaProgrammer());

by something similar to this:

Programmer programmer = ProgrammerFactory.getProgrammer();

That is factory method pattern in action.

By this you can choose the backend implementation at run time , say you pass two parameters to the above factory method (one for UI, one for Java) and based on the values you can choose a Angular/Java implementation or a Java/Node Js implementation or a React/ DotNet implementation etc.

Quoting Gang of Four,

Decouple an abstraction from its implementation so that the two of them can vary independently.

They call the Programmer base class as Abstraction , the subclass of Programmer like AngularProgrammer as RefinedAbstraction , the interface for backend implementation classes , BackendProgrammer as Implementor and the implementations of backend coding like JavaProgrammer as ConcreateImplementor.

Here is the class diagram:

As a thumb rule ,

If there are two different categories each having different possible values and the two categories need to be combined , try Bridge pattern instead of creating a subclass for each combination.

Eg) Color and Shape are two categories.

Color can have values like Blue , Red , Green

Shape can have values like Square , Circle and Rectangle

Combining Color and Shape you have possible values like BlueSquare , BlueCircle , RedSquare , GreenCircle and so on .

Instead of creating subclass for each possible value , provide a seperate class hierarchy for Shape and pass an instance of it to Color classes which can then delegate Shape related behaviour to the passed instance.

Here is the code :

https://github.com/vijaysrj/designPatternsGoF/tree/master/src/structural/bridge/pattern


Posted

in

,

by

Comments

Leave a Reply

Discover more from The Full Stack Developer

Subscribe now to keep reading and get access to the full archive.

Continue reading