Bridge Design Pattern

Introduction 

 
Hello again. We have been taught the power of inheritance, but if we use more than we are supposed to, then it loses its beauty, and unnecessarily too much use of inheritance is not a good practice in object-oriented design. 
 
To address this problem, we can use a bridge pattern, which prefers composition over inheritance.

The bridge pattern decouples an abstraction from its implementation so that the two can vary independently.
 
Let's look at the example of a company, Apple. Apple makes iPhones & Macbooks. They need both hardware and software to build these devices.
 
Let's see how UML can represent this example.
 
An iPhone needs both hardware & software, and a MacBook needs both hardware & software. We don't have to create any more classes, like iPhoneHardware or iPhoneSoftware because we can inject hardware & software classes to an iPhone or a MacBook dynamically, or if there is a new product say, an iPad.
 
Basically classes that are supposed to increase exponentially with every new product in order to satisfy their needs can be injected through the bridge.
 
Bridge Design Pattern
 
You see, iPhones & MacBooks need hardware & software to build. However, they are resolving their dependencies through a bridge like a structure. The abstract class Apple HAS-A interface IRequisite which is both hardware & software. This will be provided for our products: iPhone & MacBook.
 
Let's understand the terminology:
  1. Abstraction: abstract class Apple: the core of the bridge design pattern and contains a reference to the implementer (IRequisite).
  2. Implementer: interface IRequisite: It defines the interface for implementation classes. 
  3. Concrete class of abstraction: class iPhone & MacBook: implements the Apple(abstraction) and defines their functionalities.
  4. Concrete Implementation: class Hardware & Software:  Implements the above interface IRequisite(implementer) by providing a concrete implementation.
We must get into the code before this gets any more confusing.
 
As usual, let's begin with the interface: IRequisite: this declares product's needs.
 
Method SetFirmwareOrSoftwareVesrion() defines which software or hardware, product suppose to have. Other methods set hardware/software's name & price.
  1. namespace Bridge_Design_Pattern  
  2. {  
  3.     public interface IRequisite  
  4.     {  
  5.         string SetFirmwareOrSoftwareVesrion();  
  6.   
  7.         double SetPrice();  
  8.   
  9.         string SetFirmwareOrSoftwareName();  
  10.     }  

Let's have our first concrete class: Hardware. For now, we are only considering one hardware motherboard. 
  1. namespace Bridge_Design_Pattern  
  2. {  
  3.     public class Hardware : IRequisite  
  4.     {  
  5.         public string SetFirmwareOrSoftwareName()  
  6.         {  
  7.             return "MotherBoard FirmWare";  
  8.         }  
  9.   
  10.         public string SetFirmwareOrSoftwareVesrion()  
  11.         {  
  12.             return  "5.0";  
  13.         }  
  14.   
  15.         public double SetPrice()  
  16.         {  
  17.             return  20000;  
  18.         }  
  19.     }  

Now our second concrete class: software. Again. we are only setting up IOS info. 
  1. namespace Bridge_Design_Pattern  
  2. {  
  3.     public class SoftWare : IRequisite  
  4.     {  
  5.        
  6.         public double SetPrice()  
  7.         {  
  8.             return  15000;  
  9.         }  
  10.   
  11.         public string SetFirmwareOrSoftwareVesrion()  
  12.         {  
  13.             return  "IOS production version 13.2";  
  14.         }  
  15.   
  16.         public string SetFirmwareOrSoftwareName()  
  17.         {  
  18.             return  "IOS 13.2";  
  19.         }  
  20.     }  

Until now, we have built the right side of our bridge. Now let's make the left side.
 
We need abstraction, which has reference to this hardware & software, plus it must be implemented by our products MacBook & iPhone. It is not necessary to have an abstract class, you could have an interface instead. Just set IRequisite in properties rather than the constructor here.
  1. namespace Bridge_Design_Pattern  
  2. {  
  3.     public abstract class Apple  
  4.     {  
  5.         IRequisite RequiredHardware { getset; }  
  6.         IRequisite RequiredSoftware { getset; }  
  7.   
  8.         public Apple(IRequisite hardware, IRequisite software)  
  9.         {  
  10.             this.RequiredHardware = hardware;  
  11.             this.RequiredSoftware = software;  
  12.         }  
  13.         public abstract string SetProductName();  
  14.         public abstract double SetProductPrice();  
  15.   
  16.         public void SetHardware() {  
  17.             System.Console.WriteLine("---------Software Details---------");  
  18.             System.Console.WriteLine(" Name: "+RequiredHardware.SetFirmwareOrSoftwareName());  
  19.             System.Console.WriteLine(" Version: "+RequiredHardware.SetFirmwareOrSoftwareVesrion());  
  20.             System.Console.WriteLine(" Price: "+RequiredHardware.SetPrice());  
  21.         }  
  22.         public void SetSoftware()  
  23.         {  
  24.             System.Console.WriteLine("---------Hardware Details---------");  
  25.             System.Console.WriteLine(" Name: " + RequiredSoftware.SetFirmwareOrSoftwareName());  
  26.             System.Console.WriteLine(" Version: "+RequiredSoftware.SetFirmwareOrSoftwareVesrion());  
  27.             System.Console.WriteLine(" Price: " + RequiredSoftware.SetPrice());  
  28.         }  
  29.     }  

Let's implement our first product: iPhone: the purpose of a parameterized constructor is to solve dependencies. This is, in fact, the raw material for our bridge. 
  1. namespace Bridge_Design_Pattern  
  2. {  
  3.     public class iPhone : Apple  
  4.     {  
  5.         
  6.         public iPhone(IRequisite hardware, IRequisite software): base(hardware, software)  
  7.         {  
  8.   
  9.         }  
  10.   
  11.          
  12.         public override string SetProductName()  
  13.         {  
  14.            return "IPhone X";  
  15.         }  
  16.   
  17.         public override double SetProductPrice()  
  18.         {  
  19.             return  110000;  
  20.         }  
  21.     }  

Now let's make MacBook:
  1. namespace Bridge_Design_Pattern  
  2. {  
  3.     public class MacBook : Apple  
  4.     {  
  5.        
  6.         public MacBook(IRequisite hardware, IRequisite software) : base(hardware, software)  
  7.         {  
  8.   
  9.         }  
  10.   
  11.         public override string SetProductName()  
  12.         {  
  13.             return  "Apple MacBook Pro";  
  14.         }  
  15.   
  16.         public override double SetProductPrice()  
  17.         {  
  18.             return  170000;  
  19.         }  
  20.     }  

At last, our caller. The caller doesn't even need to know from where the hardware or software is coming from. This we have handled through our pattern.
  1. using System;  
  2.   
  3. namespace Bridge_Design_Pattern  
  4. {  
  5.     class Program  
  6.     {  
  7.         static void Main(string[] args)  
  8.         {  
  9.             Apple Iphone = new iPhone(new Hardware(), new SoftWare());  
  10.             Console.WriteLine("---- iPhone Details ----");  
  11.             Console.WriteLine($" Product: {Iphone.SetProductName()} \n Price: {Iphone.SetProductPrice()}");  
  12.             Iphone.SetSoftware();  
  13.             Iphone.SetHardware();  
  14.             Apple MacBook = new MacBook(new Hardware(), new SoftWare());  
  15.             Console.WriteLine(Environment.NewLine + Environment.NewLine +"---- MacBook Pro Details ----");  
  16.             Console.WriteLine($" Product: {MacBook.SetProductName()} \n Price: {MacBook.SetProductPrice()}");  
  17.             MacBook.SetSoftware();  
  18.             MacBook.SetHardware();  
  19.         }  
  20.     }  

Beautiful! Let's run our project & see the magic...
 
 
Alright!
 
We have successfully implemented a bridge patten.
 
I sincerely hope you enjoyed this article and that you're inspired to apply what you've learned to your own applications.
 
Thank you, & Happy coding.
 
If you have any doubts, connect with me.