Bridge Pattern in PHP | Using Design Patterns in 2023

Last Updated on

CraftyTechie is reader-supported. When you buy through links on our site, we may earn an affiliate commission.

Using the Bridge Design Pattern in PHP

The bridge design pattern in PHP is a structural programming design pattern to form independent hierarchies of abstractions and implementations. Abstractions are high-level modules that use implementations to carry out operations. There is a bridge or a connector in the form of the composition relationship to bind these hierarchies. 

Bridge Pattern in PHP
Bridge Pattern in PHP

If all these sound too abstract, don’t worry because we’ll see that in much depth. So stay tuned to learn more about this pattern.

Definition | Bridge Design Pattern in PHP

“The bridge design pattern is a structural design pattern  that separates abstraction and implementation into separate hierarchies and lets them vary independently.”

Bridge Pattern in PHP
Bridge Design Pattern

The objective of this pattern is to separate the interface and implementation from loosening up tight coupling and let them vary independently. 

App Repository | A Real-Life Example

Let’s assume we want to add a repository class to our hypothetical E-commerce application. A repository class is a high-level class that is supposed to hide the implementation details of a storage facility used behind the scenes.

A client program uses a repository class and doesn’t bother about the storage destination. The repository class is supposed to promote flexibility in terms of what storage capacity we use in our applications.

The BaseRepository and its subclasses 

The repository class is a base class for the concrete repository classes: Customer and seller repositories.

An evident design for this feature will use inheritance which is as follows.

Repository Class
Repository Class

That seems okay as long as we are unaware of the complete picture. The idea of a repository class/interface is to add a persistence layer to the application without giving away alot of implementation details. Nevertheless, it needs a storage capacity on the backend to communicate with. Otherwise, it is good for nothing.

But storage comes in different forms and flavors

The choice of storage largely depends on the use case. We typically use a relational database for mapping relationships and storing tabular data. We can use object-based storage in the cloud for static assets. Moreover, we can use file storage for application logs. That’s only a few examples.

So, let’s say we want to include a database and object storage in our application. How do you modify your existing design to have a variant of repository classes for each storage? The only way that seems apparent may look as follows.

Bridge Pattern in PHP


So, what’s wrong with this implementation?

Exponentially growing hierarchy

We add two variants for every repository class we introduce. Suppose our application has ten repositories, each with two variants. Simple maths suggests the number of sub-classes would be 20. That’s a huge number, though it is a conservative estimate because E-commerce applications may have more than ten repositories.

Another dimension is the storage class. Suppose we add another type of storage – let’s say file storage. Ten repositories, each with three variants, span 30 sub-classes. So, a small change has a massive impact increasing complexity two folds. But that’s not the only problem.

Code Duplication

Code duplication is an obvious problem when you have classes that share much in common. For instance – the repository classes that use object-based storage would have repetitive code for common operations. This issue is inherent in the design because interfaces are coupled with implementations (More on this later). A more generic solution is possible that avoids such duplication.

Inflexible, non-maintainable code

That’s a usual outcome of a bad design. A minor change may have a ripple effect on all those sub-classes and code duplication. Do you want to change the database? Good luck with that because that’s alot of modification with a high probability of introducing a new bug and breaking existing working code.

When a minor change becomes a nightmare, know that you have an inflexible and non-maintainable design. We need flexible systems open to changes and extensions because change is only permanent in software development.

Open-closed principle

The open-closed principle states that “A class should be closed to modification and open to extension.” As we have already mentioned, a minor change may force you to change the existing repository classes. For instance – adding or replacing the database. You have to manually go to each repository class that uses a database and make changes to the existing code.

Problem 🙁

Let’s zoom in on the current design. The design couples interface to implementation in a way that it is one unit. The downside is that we can’t change, evolve or extend these independently. Any change in one has a profound impact on the other.

Tightly coupled modules
Tightly coupled modules

Let’s also take this moment to clarify what we mean by interface and implementation in the current context.

Interface & implementation

In this context, interface means a higher-level entity or presentational layer. It is not supposed to do the actual job rather it delegates it to the underlying implementational layer. It doesn’t refer to interfaces or abstract classes in programming.

The interface is an abstraction, a curtain over what’s happening backstage. Good software systems program to interfaces, not implementations, because implementations are specialized pieces of code with little flexibility to accommodate change. Interfaces are generic and flexible.

So, the client program should see interfaces only. The interface should expect calls from the client and delegate them to the workers underneath.

Solution 🙂

Segregating hierarchies

So, as we have seen already, we have an interface hierarchy and implementation. To resolve the coupling issue, we separate the classes into two hierarchies.

  • Abstraction: BaseRepository class and its sub-classes.
  • Implementation: Storage class and its sub-classes.
Interface & Implementation
Interface & Implementation

Bridging the gap

So, that’s one piece of the puzzle. Now, we need to connect these two hierarchies. To do so, we will prefer composition over inheritance. As a refresher, the composition is when a class has a reference to an object of another class. 

Composition, also called has-a relationship, is a strong relationship and lets the base class call methods on the object of another class, delegating responsibilities. Let’s connect the two hierarchies.

Bridge Pattern in PHP
Bridging the gap

Benefits

  • Loosely coupled code with interface and implementation hierarchies segregation.
  • Modify or extend either hierarchy without affecting the other.
  • Option to switch Storage class dynamically.
  • Flexible and maintainable design.
  • The client program uses the interface without having to worry about implementations.

Complete Architecture | Bridge Design Pattern in PHP

Bridge Design Pattern in PHP
Bridge Design Pattern in PHP

Bridge Design Pattern in PHP

The following is a complete example of bridge design pattern in PHP.

Code | Bridge Design Pattern in PHP

<?php
 
//Implementation hierarchy
interface Storage {
 
    function save();
    function retrieve();
    function update();
    function delete();
}
 
class DatabaseStorage implements Storage {
 
    function save() {
        echo "Saving record(s) to database".PHP_EOL;
    }
 
    function retrieve() {
        echo "Retrieving record(s) from database".PHP_EOL;
    }
 
    function update() {
        echo "Updating record(s) in database".PHP_EOL;
    }
 
    function delete() {
        echo "Deleting record(s) from database".PHP_EOL;
    }
}
 
class ObjectStorage implements Storage {
 
    function save() {
        echo "Saving record(s) to object-store".PHP_EOL;
    }
 
    function retrieve() {
        echo "Retrieving record(s) from object-store".PHP_EOL;
    }
 
    function update() {
        echo "Updating record(s) in object-store".PHP_EOL;
    }
 
    function delete() {
        echo "Deleting record(s) from object-store".PHP_EOL;
    }
}
 
 
//Interface hierarchy
interface BaseRepository {
 
    function create();
    function get();
    function update();
    function delete();
}
 
class CustomerRepository implements BaseRepository {
   
    private $storage;
 
    function __construct($storage) {
        $this->storage = $storage;
        echo "Creating Customer Repository".PHP_EOL;
    }
 
    function create() {
        $this->storage->save();
    }
 
    function get() {
        $this->storage->retrieve();
    }
 
    function update() {
        $this->storage->update();
    }
 
    function delete() {
        $this->storage->delete();
    }
}
 
class SellerRepository implements BaseRepository {
   
    private $storage;
 
    function __construct($storage) {
        $this->storage = $storage;
        echo "Creating Seller Repository".PHP_EOL;
    }
       
    function create() {
        $this->storage->save();
    }
 
    function get() {
        $this->storage->retrieve();
    }
 
    function update() {
        $this->storage->update();
    }
 
    function delete() {
        $this->storage->delete();
    }
}
 
function main() {
 
    $database = new DatabaseStorage();
    $object   = new ObjectStorage();
 
    $sellerRepo = new SellerRepository($database);
    $sellerRepo->create();
    $sellerRepo->get();
    $sellerRepo->update();
    $sellerRepo->delete();
 
    $customerRepo = new CustomerRepository($object);
    $customerRepo->create();
    $customerRepo->get();
    $customerRepo->update();
    $customerRepo->delete();
}
 
main();
?>

Output | Bridge Design Pattern in PHP

Creating Seller Repository
Saving record(s) to database
Retrieving record(s) from database
Updating record(s) in database
Deleting record(s) from database
Creating Customer Repository
Saving record(s) to object-store
Retrieving record(s) from object-store
Updating record(s) in object-store
Deleting record(s) from object-store

Pros and Cons of the Bridge Pattern in PHP

ProsCons
The client calls interface methods, not implementation.If you have a highly specialized class, then applying this pattern can usually be overkill.
The interface is loosely coupled to implementation. 
Interface and implementation can be extended or changed independently.
It uses composition, which is a stronger relationship than inheritance.
Satisfies the open-closed principle.

Where is the Bridge Pattern Used?

  • Use the bridge pattern to break down a monolithic class into separate hierarchies.
  • You want to add features or variants of a certain feature to the interface without creating a chain reaction.
  • Loosen tight coupling between inter-related modules of your application.
  • You want to switch implementation dynamically at runtime.

Frequently Asked Questions on the bridge design pattern

What type of design pattern is the bridge?

The bridge design pattern is a structural design pattern that separates abstraction and implementation into separate hierarchies and lets them vary independently.

What is the difference between adapter and bridge design patterns?

Adapter pattern is useful when you have two incompatible classes that cannot work because they have different interfaces. The adapter is a class that acts as an intermediary converter for an interface and makes it compatible with the other class. It is useful when you have a library or a third-party service that does’ t fit in the target module. An adapter can help in the integration.

The bridge pattern helps decouple interface and implementation in separate orthogonal hierarchies so that when one hierarchy changes, it doesn’t affect the other. It is helpful when you have a monolithic module with many interrelated features that can form its own hierarchy.

When to use the bridge design pattern in PHP?

Use it when you have tightly coupled inter-related interfaces and implementation classes. Separate them into their own orthogonal hierarchies and link the implementation object back to the interface using composition. So, the interface can delegate operations to the implementation object. 

Using the Bridge Pattern in PHP Today

Cool! We have seen the bridge pattern and the problem it tries to solve. As a recap, the bridge pattern is a structural design pattern that allows you to separate abstractions and implementations in isolated hierarchies so that one may not affect the other in the aftermath of a change.

We demonstrated a real-life example by designing a repository module. This module has two sub-classes, Customer and Seller. When we add a couple of storage options, the repository module increases twofold because now we have two variants of each repository class.

Later, we identified that the issue was a tight coupling between two aspects of our application – repository class and storage class. A repository class is supposed to be an abstraction for the type of storage class rather than housing concrete storage implementation logic.

So, in the next few steps, we divide the module into two hierarchies: Abstraction and Implementation. The repository class, an abstraction, then delegates operations to the storage implementor class by keeping a reference to its object (composition).

As a result, we design a software system that includes two independent hierarchies that are much more stable and expandable. 

So, that’s all about it. We hope you are now well aware of this design pattern and can implement it whenever possible.

See you in another design pattern article. Stay tuned at FuelingPHP.

Books on Design Patterns

Want to learn more about Design Patterns? There are many great resources online. We recommend the following books for your collection as they both can teach you the theoretical and the application of using design patterns in your day-to-day programming. Feel free to use the following Amazon affiliate links if you’d like to purchase them and a way to support our efforts.

Design Patterns: Elements of Reusable Object-Oriented Software

Design Patterns: Elements of Reusable Object-Oriented Software book

This is the book that started it all. I believe that every programmer should have a referenced copy to this book at some point in their career. There have been many updates and excellent newer content through the years, but this is a classic. It still stands the test of time and is just as relevant for today as it was in the original printing in the 90s.

Check it out on amazon

Learning PHP Design Patterns

This is an excellent book to go beyond the theory and apply it to writing good PHP code. Learning PHP Design Patterns is published by the popular O’Reilly media company. O’Reilly consistently publishes some of the most useful reference material related to software development. They are known to provide materials that thoroughly cover a topic in a way that is simple to understand. I recommend this book to every PHP developer.

Check it out on amazon

Want to see our full review of books on design patterns? Read our huge review article on over 15 design pattern books.

Design Patterns in PHP Learning Series.

This article is part of our series of design patterns in PHP. We are going through all of the patterns and showing how they can help you build better applications. Browse through our full list of patterns below.

Did you find this article helpful?

Join the best weekly newsletter where I deliver content on building better web applications. I curate the best tips, strategies, news & resources to help you develop highly-scalable and results-driven applications.

Build Better Web Apps

I hope you're enjoying this article.

Get the best content on building better web apps delivered to you.