One of the most badly explained pattern, even the name doesn't justify what is it trying to do. Based on my understanding from various other resources. Bridge pattern is one way of telling someone that prefer composition over inheritance.
There are 3-4 heavy weight words being hammered into your head until you give upon trying to understand this structural pattern.
Let's see what are the goals of structural pattern.
Structural patterns guide you on how classes and objects are composed to form larger structures. Let's try to compare it with some analogy from the database. Storing employee records can require n number of columns you begin with one simple table and then improve by splitting into multiple tables and normalize the columns for the optimal design. In this case you structured your data.
Now let's say you need to represent that same employee record in your code, you can have on gigantic class that doesn't care about anything other than being functional for one time or you can structure it by splitting it into multiple classes with single responsibility and compose them together to finally represent the employee object.
Question: What advantage did we got from splitting data into multiple tables?
Answer: Easy identifiable advantages are each table can scale independently, if needed fewer tables can be queried and reduced duplication
The goals for structural design patterns are same. Bridge design pattern is called so because when you draw UML diagrams it starts representing a structure as bridge.
First point of focus while trying to understand the Bridge Pattern is prefer composition over inheritance.
Second point of focus is what you are trying to achieve utilizing bridge pattern, structuring classes to avoid exploding number of classes to implement the variations of an object. e.g. instead of having 15 different implementations you want have something like 3*5 solution.
Third point of focus if you have created 15 different variations by sub-classing then to use them you need to instantiate each specific implementation and these classes may not have the same interface so your client is bound to the implementations of these classes.
Fourth point of focus identify the mutually independent parts of you classes and split them so that they can be reused with more classes.
At this moment we see a need of having extensibility in our code without breaking the clients implementation, whenever there is a new variation added.
This particular pattern can be compared with the Adapter pattern and one of the major difference would be that this would look more like a planned Adapter pattern.
Why the most common shapes example is fruitless for understanding of bridge pattern?
Shapes example is pretty basic where the abstractions are colors and they set color attribute to the the various shapes, when I first came across with the example my reaction was, why would anyone extend the class only for having a different color, moreover you can set this attribute in the constructor itself.
A good example is extremely important to get the understanding of bridge pattern. Implementation offer some barebones functionality using which abstractions implement the variations and this implementation would look like adapter pattern since ultimately your abstraction internally calls the functions implemented by the concrete implementations that offered barebones.
I find the remotes and devices example more helpful in understanding this concept. Remote implementations give you volume up and down methods and if let's say you need an advanced remote with the mute button you can create a new abstraction around it and expose the mute function that internally keeps calling the volume down function until volume is 0 or setVolume(0) is there exists a function like that.
So in this case using one remote class (actual implementation), you could create advanced remote, now as longs as any remote in the world has volume up and down functions exposed this structure could generate an advanced variation of it without you explicitly extending each class (isn't this an adapter pattern already?).
Yes this is implemented using adapter pattern but the difference is that in adapter we already know what interface we want to convert to, here we are doing it in the planning phase and the objective is not just to adapt but to add to the functionality in a way that it can be built using the barebones functionality provided in implementation classes.