Design patterns: Bridge
Hello everyone! Today topic is about the Bridge design pattern however, we will change some form of the entry into a more structured one, so let’s get to the topic 🙂
Intent
- Separates abstractions from implementation.
- The classes are not supposed to know about themselves.
The problem that it solves:
- The possibilities of the class are greater (m.i.n by inheritance)
- We maintain the principle of hermetization
- The modules are independent of each other
- The client does not see the implementation, so when we changing the implementation, we will not have to change the interface.
Use when:
- You have many classes that inherit from the abstract class.
- We want to separate the implementation from the client.
- You must plan the class hierarchy.
- You want to split the implementation between multiple objects.
Also when:
A
/ \
Aa Ab
/ \ / \
Aa1 Aa2 Ab1 Ab2
Then the Bridge pattern allows you to change to:
A N
/ \ / \
Aa(N) Ab(N) 1 2
Discussion
As the saying goes “Composition over inheritance“. Is it true? In some cases, yes, if it is as in the example above, we have a lot of derived classes in this example:
It’s better to create something based on this using the Bridge pattern:
Structure
Below is the UML diagram of the Bridge, which clearly shows that the client does not want to know the details of the implementation, and that the object’s abstraction is separated from the implementation.
Example
And now an example in the form of a code:
class Program
{
static void Main(string[] args)
{
var classimplementationfirst = new BridgeImplementation(new ClassImplementationFirst());
classimplementationfirst.GetMethod();
Console.ReadKey();
}
}
abstract class Bridge
{
private IIntefaceEncapsulation interfaceencapsulation;
public Bridge(IIntefaceEncapsulation interfaceencapsulation)
{
this.interfaceencapsulation = interfaceencapsulation;
}
public void DoMethodOne()
{
interfaceencapsulation.DoMethodOne();
}
public void DoMethodTwo()
{
interfaceencapsulation.DoMethodTwo();
}
}
class BridgeImplementation : Bridge
{
public BridgeImplementation(IIntefaceEncapsulation interfaceencapsulation):base(interfaceencapsulation){}
public void GetMethod()
{
DoMethodOne();
DoMethodTwo();
}
}
interface IIntefaceEncapsulation
{
void DoMethodOne();
void DoMethodTwo();
}
class ClassImplementationFirst : IIntefaceEncapsulation
{
public void DoMethodOne()
{
throw new NotImplementedException();
}
public void DoMethodTwo()
{
throw new NotImplementedException();
}
}
class ClassImplementationSecond : IIntefaceEncapsulation
{
public void DoMethodOne()
{
throw new NotImplementedException();
}
public void DoMethodTwo()
{
throw new NotImplementedException();
}
}
Of course, the abstraction is the Bridge class, using dependency injection we inject into the constructor of the BridgeImplementation class, which inherits from the Bridge class the object of the class we want to operate on and we call its methods in the client.
A practical example of life taken
In order to better understand the separation of the implementation from the object’s abstraction, I will use a picture that I found somewhere in the network that illustrates well the operation of the bridge, that is separating the implementation from the interface.
The abstraction is the Class of the Bridge, which illustrates what the button is supposed to do eg on the TV, the TV, radio or lamp object is already an implementation.
I will give an example from the picture, only we will move it to the code.
class Program
{
static void Main(string[] args)
{
Switch tvButton = new ClickSwitch(new TV());
tvButton.On();
tvButton.Off();
Switch radioButton = new ClickSwitch(new Radio());
radioButton.On();
radioButton.Off();
Switch lampButton = new ClickSwitch(new Lamp());
lampButton.On();
lampButton.Off();
Console.ReadKey();
}
}
abstract class Switch
{
protected IDevice device;
public Switch(IDevice device)
{
this.device = device;
}
public abstract void On();
public abstract void Off();
}
class ClickSwitch : Switch
{
public ClickSwitch(IDevice device):base(device)
{ }
public override void On()
{
device.On();
}
public override void Off()
{
device.Off();
}
}
interface IDevice
{
void On();
void Off();
}
class TV : IDevice
{
public void Off()
{
Console.WriteLine("the TV was turned off");
}
public void On()
{
Console.WriteLine("the TV was turned on");
}
}
class Radio : IDevice
{
public void Off()
{
Console.WriteLine("the radio was turned off");
}
public void On()
{
Console.WriteLine("the radio was turned on");
}
}
class Lamp : IDevice
{
public void Off()
{
Console.WriteLine("the lamp was turned off");
}
public void On()
{
Console.WriteLine("the lamp was turned on");
}
}
The bridge is here the abstract class Switch, implementation is the TV, Radio, Lamp classes.
The case looks similar to the previous example, to the class ClickSwitch, which inherits from the Switch class, we pass the class object to the constructor using dependency injection and call the On and Off methods.
Result
Relations with other design patterns
The Bridge pattern is quite similar to a facade or adapter. However, the facade differs mainly in that it simply has to provide a simple interface to the client, and the adapter has to combine non-matching interfaces, with smaller projects the implementation of these patterns does not differ much, however, in larger projects there are big differences.
Summary
That’s all about Bridge 🙂.
Link to github with the whole code from this article: https://github.com/Slaw145/BridgeTutorial
This content also you can find on my blog http://devman.pl/programtech/design-patterns-bridge/
If you recognise it as useful, share it with others so that others can also use it.
Leave upvote and follow and wait for next articles :) .
In the next article, we will talk about the Proxy pattern.
And NECESSERILY join the DevmanCommunity community on fb, part of the community is in one place 🙂
– site on fb: Devman.pl-Sławomir Kowalski
– group on fb: DevmanCommunity
Ask, comment underneath at the end of the post, share it, rate it, whatever you want🙂.
Take care 🙂