Mixing abstractions and implementations in the same inheritance hierarchy can lead to code that's confusing and difficult to maintain.
For example, in the following hierarchy Computer, Server, and Desktop are abstractions, while Dell and Mac are implementations:
Notice that representing a Dell desktop requires two objects: an instance of desktop and an instance of Dell. Representing a single object in an application domain by several objects in a model can lead to synchronization problems. For example, when the warranty expires on the real world Dell desktop, we may need to clear a warrantyValid flag in two places. Clearing it in one place and forgetting to clear it in the other place leads to an inconsistent model and possible errors.
We could introduce special Mac and Dell subclasses of Server and Desktop (or Server and Desktop subclasses of Dell and Mac):
This might make sense if, for example, MacServer and MacDesktop were significantly different, but otherwise this solution will introduce unwanted code replication. It can also lead to a combinatorial explosion in the number of classes we may need to introduce. For example, suppose we want to expand this library to include HP and LapTop. This will require declaring six new classes. (What are they?)
The Bridge Pattern solves this problem by creating two hierarchies: abstractions and implementations. The hierarchies are connected at their roots by a bridge:
The operations inherited from the Computer class are implemented by delegating to the peer, which can be an instance of Mac or Dell. Changing implementations simply involves changing the peer, no change to the code in the abstraction hierarchy (Computer, Server, Desktop) is necessary.
The Bridge Pattern is often combined with the Adapter Pattern when there are pre-existing implementations that don't implement the root of the implementation hierarchy: