Design Pattern: Bridge vs Strategy

Context

Last week, while digging around composition design in software development, I found a question that many people asked about design pattern:.

What is the difference between the bridge pattern and the strategy pattern?

Although these two design patterns are different, their UMLs are similar, the implementation are also similar. Some people even say they are the same thing, just being used for different purpose.

In this post, I will explain the definitions of Bridge and Strategy with some examples, and the differences between them.

Strategy

The strategy pattern is a Behavioral pattern

Gang of Four definition:

“Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it”

This pattern contains one abstract strategy interface and many concrete strategy implementations (algorithms) of that interface. The application uses strategy interface only. Depending in some configuration parameter, the concrete strategy will be tagged to interface.

When to use Strategy:

  • You have more ways (multiple algorithms) for doing an operation
  • You want to choose the algorithm at run-time (how your object behave)
  • You want to modify a single Strategy without a lot of side-effects at compile-time

design-pattern-strategy-template.png Image from refactoring.guru

A lot of resources on the Internet use the word “context” when explaining strategy pattern. Simply put, “context” is the class contains the operation (method) Context tied to the Strategy: The “context” would know/contain the strategy interface reference and the implementation to invoke the strategy behavior on it.

Example:

I want to build a navigation application where user can find route from one point to another. And user can choose which way they want to take (by train, by car, on foot etc)

Here we have:

  • Context is the navigation app
  • Operation is finding a route
  • Multiple algorithms are ways to navigate
  • Choosing/switching algorithms at run-time: user can choose which method to navigate

The UML Diagram for the sample will look like this:

design-pattern-strategy.png

Sample code:

<?php

class Navigator
{
    private $transportMode;

    public function __construct(TransportMode $transportMode)
    {
        $this->transportMode = $transportMode;
    }

    public function setStrategy(TransportMode $transportMode)
    {
        $this->transportMode = $transportMode;
    }

    public function findRoute(): void
    {
        echo "Navigator: find route using current transport mode (which I don't know)" . "<br>";
        $result = $this->transportMode->findRoute();
        echo "Navigator: " . $result;
    }
}

interface TransportMode
{
    public function findRoute();
}

class Bus implements TransportMode
{
    public function findRoute()
    {
        return "Found a bus for you!";
    }
}

class Train implements TransportMode
{
    public function findRoute()
    {
        return "Found a train for you!";
    }
}

class Pedestrian implements TransportMode
{
    public function findRoute()
    {
        return "Go ahead...";
    }
}

$navigator = new Navigator(new Bus());
echo "Client: I want to use bus.\n";
echo "<br>";
$navigator->findRoute();
echo "<br>";

echo "Client: Now I want to use train!.\n";
$navigator->setStrategy(new Train());
echo "<br>";
$navigator->findRoute();
echo "<br>";

echo "Client: Never mind, I just walk over there...\n";
$navigator->setStrategy(new Pedestrian());
echo "<br>";
$navigator->findRoute();

Result:

Client: I want to use bus.
Navigator: find route using current transport mode (which I don't know!)
Navigator: Found a bus for you!
Client: Now I want to use train!.
Navigator: find route using current transport mode (which I don't know!)
Navigator: Found a train for you!
Client: Never mind, I just walk over there...
Navigator: find route using current transport mode (which I don't know!)
Navigator: Go ahead...

Bridge

The bridge pattern is a Structural pattern

Gang of Four definition:

“Decouple an abstraction from its implementation so that the two can vary independently” Let’s analyze the definition: we have “abstraction” and “implementation”

  • Abstraction: a high-level interface that contains non-specific implementation of any control logic (or business logic)
  • Implementation (or “logic”): responsible for the low-level work, separated from abstraction

When to use Bridge:

  • You have many implementations
  • Implementation and abstraction are developed independently/separated
  • Abstraction doesn’t know the implementation
  • Client can only access to abstraction, any changes made to implementation should not affect client

design-pattern-bridge-template.png Image from refactoring.guru

Example:

We will continue with our navigation application above. Now the user can choose the route, and we need to implement the feature. There will be some constraints:

  1. There will be 2 platforms: smartphone and smartwatch. The application will have different behavior due to differences in hardware (sensor etc)
  2. Depend on the manufacturer, Google service availability is different. Our application has to:
    1. use Google map and Google direction service on Google service supported device
    2. use OpenStreetMap and Open Route Service on Google service non-supported device

We will also assume that user doesn’t care which service we use, as long as they can get to where they want.

Here we have:

  • Abstraction is the navigator
  • Implementation are Google service and OpenStreetMap service

The UML Diagram for the sample will look like this:

design-pattern-bridge.png

Sample code:

<?php
abstract class Navigator
{
    protected $routingRenderer;
    protected $isGoogleSupported;

    public function __construct()
    {
        // Here I use rand to simulate which service we will use
        // In real life situation, we can use device API to check
        $this->isGoogleSupported = (bool)rand(0,1);
        if ($this->isGoogleSupported) {
            $this->routingRenderer = new GGserviceRenderer();
            echo "This device supports Google Service";
            echo "<br>";
        } else {
            $this->routingRenderer = new OpenStreetMapRenderer();
            echo "This device doesn't support Google Service";
            echo "<br>";
        }
    }

    abstract public function renderRoute();
}

class SmartphoneNavigator extends Navigator
{
    public function renderRoute() {
        echo "Render route on smartphone: ";
        $this->routingRenderer->drawRoute();
    }
}

class SmartwatchNavigator extends Navigator
{
    public function renderRoute() {
        echo "Render route on smartwatch: ";
        $this->routingRenderer->drawRoute();
    }
}

interface RoutingRenderer {
    public function drawRoute();
}
class GGserviceRenderer implements RoutingRenderer {
    public function drawRoute() {
        echo "Draw Google Map";
    }
}
class OpenStreetMapRenderer implements RoutingRenderer{
    public function drawRoute() {
        echo "Draw Open Street Map";
    }
}

for ($i=0 ; $i<3 ; $i++) {
    $smp = new SmartphoneNavigator();
    $smp->renderRoute();
    echo "<br><br>";
}
for ($i=0 ; $i<3 ; $i++) {
    $smw = new SmartwatchNavigator();
    $smw->renderRoute();
    echo "<br><br>";
}

Result:

This device doesn't support Google Service
Render route on smartphone: Draw Open Street Map

This device supports Google Service
Render route on smartphone: Draw Google Map

This device supports Google Service
Render route on smartphone: Draw Google Map

This device doesn't support Google Service
Render route on smartwatch: Draw Open Street Map

This device supports Google Service
Render route on smartwatch: Draw Google Map

This device doesn't support Google Service
Render route on smartwatch: Draw Open Street Map

The difference between Bridge and Strategy

We will look at the two UMLs again: comparison.jpg Bridge will completely hide the concrete implementation from the client, while in strategy, the client can “inject” the implementation. Bridge pattern is mostly used where you have many classes that are orthogonal with each other (sometimes called two dimensions in the domain)

        A
     /     \
    Aa      Ab
   / \     /  \
 Aa1 Aa2  Ab1 Ab2

References:

GeeksforGeeks - Bridge Design Pattern

The GoF Hot Spots - Bridge vs Strategy

Game Engineering - Bridge Pattern vs Strategy Pattern