tutorial, window-inheritance, architecture,

02.Bridge - window-inheritance

Upendra Upendra Follow Jan 21, 2025 · 5 mins read
02.Bridge - window-inheritance
Share this

Bridge

Intent

Decouple abstraction from implementation so that they can vary independently.

Motivation

A typical way to extend an abstraction which can have multiple implementations is to use inheritance. This approach isn’t always ideal because it binds the implementations to the abstraction permanently. This makes it difficult to modify & reuse the abstractions & implementations independently.

Example - a Window class which can have different implementations for different platforms.

Initially, using inheritance does the trick, but if the class needs to support different inheritance dimensions - eg platform-specific implementations & window-type-specific implementations, this can result in an explosion of classes.

Window inheritance diagram

In addition to this, this approach makes the client code platform dependent, because the client has to choose which platform-specific class to instantiate - eg XWindow or PMWindow. Porting the code to a new platform would require changes in the code & recompilation.

The Bridge pattern addresses these two problems by having separate class hierarchies for platform-specific windows & different window types:

Bridge example

This decouples the window abstractions from the platform-specific implementations.

Applicability

Use the Bridge pattern when:

  • you want to avoid a binding between an abstraction and an implementation. This can be useful if the implementation needs to be switched or determined at runtime.
  • Both the abstraction and the implementation have to be extensible by subclassing
  • Changes in the implementation should not impact clients - they shouldn’t have to recompile
  • You have a class explosion when an inheritance hierarchy is trying to represent multiple dimensions
  • You want to reuse an implementation across multiple clients

Structure

Bridge Structure

Participants

  • Abstraction (Window) - defines the abstractions interface & maintains a reference to the implementation
  • RefinedAbstraction (IconWindow) - a concrete implementation of the Abstraction interface
  • Implementor (WindowImp) - Defines the implementation interaface. This interface can vary greatly from the abstractions interface.
  • ConcreteImplementor (XWindowImp, PMWindowImp) - a concrete implementation of the Implementor interface.

Consequences

  • Decoupling interface & implementation
    • an implementation’s interface can vary independently of the abstraction
    • the implementation can be specified & changed at run-time
    • changing the implementation doesn’t cause a change in the abstraction & doesn’t require recompilation of that part of the system
  • Hiding implementation details - clients are unaware of how the implementation works and if there are any tricks behind the scenes - eg reusing an implementation for multiple abstractions

Implementation

Issues to consider:

  • Only one implementor
    • if you have a single implementation, you could omit the Implementation interface & use a concrete class directly. This is a degenerate version of the Bridge pattern.
    • however, you should consider that this approach does not shield the abstraction from changes in the implementation, leading to recompilation
  • How, when and where to instantiate the implementation - there are multiple options
    • do that in the Abstraction constructor so that clients don’t need to make that call
    • extract that in a separate factory so that the Abstraction doesn’t depend on concrete implementations
    • dynamically change the implementation at runtime once conditions change - eg a collection grows beyond a certain point, change the internal implementation to best suit the scale

Sample Code

Example abstraction (Window) and implementation (WindowImpl) interfaces:

public abstract class Window {
  private final WindowImpl impl;

  public Window(WindowImpl impl) {
    this.impl = impl;
  }
    
  abstract void open();
  abstract void close();
  abstract void iconify();

  // etc...
}

public abstract class WindowImpl {
  abstract void top();
  abstract void bottom();
  abstract void setExtent(Point p);

  // etc...
}

Example Window implementation - IconWindow. It leverages the WindowImpl instance to make changes to the screen:

public class IconWindow extends Window {
  @Override
  public void drawContents() {
    getWindowImpl().deviceBitmap(bitmapName, 0, 0);
  }
}

The bitmapName is some private IconWindow field the class maintains.

Different WindowImpl implementations hold different internals & implement the operations differently.

How do the implementations get constructed?

public WindowImpl getImpl() {
  if (impl == null) {
    impl = WindowImplFactory.getInstance();
  }
    
  return impl;
}

An Abstract Factory can create & configure a particular Bridge.

Adapter is usually used to extend an existing system, while Bridge is often applied as an up-front design for a system, expected to get extended.

credit goes to @preslavmihaylov
Join Newsletter
Get the latest news right in your inbox. We never spam!
Upendra
Written by Upendra Follow
Hi, I am Upendra, the author in Human and machine languages,I don't know to how 3 liner bio works so just Connect with me on social sites you will get to know me better.