Design patterns: Bridge

Mauto 3 min

The Bridge Pattern decouples an abstraction from its implementation, allowing the two to vary independently. This pattern is useful when you want to avoid an explosion of subclasses caused by multiple dimensions of variation.

Characteristics of the Bridge pattern

  1. Separation between abstraction and implementation: The Bridge Pattern separates the abstraction (interface or high-level logic) from the implementation (concrete details), allowing them to vary independently.
  2. Flexibility and extensibility: It promotes extensibility, letting you add new abstractions and implementations without modifying existing code. This makes the system more flexible and easier to adapt to new requirements.
  3. Reduced complexity: It reduces the number of classes needed compared to the traditional approach, avoiding a proliferation of subclasses. It lets you combine different abstractions and implementations without creating multiple hierarchies.
  4. Support for component independence: The abstraction doesn’t depend on the implementation, and vice versa. This means you can change the implementation without affecting the abstraction and vice versa, keeping the two components independent.
  5. Better maintainability: Separating responsibilities between abstraction and implementation makes the code easier to maintain. Changes can be made in one part without affecting the other, reducing the risk of introducing bugs.
  6. Dynamic combinations: It lets you dynamically combine different abstractions and implementations at runtime, improving operational flexibility.
  7. Use in multi-platform systems: The Bridge Pattern is especially useful when a system has to support multiple platforms, because it lets you keep a single abstract interface that can work with different platform-specific implementations.
  8. Reduced coupling: It promotes loose coupling between classes, since the abstraction and the implementation are completely separate. This reduces direct dependencies between components.

Example in PHP:

Let’s imagine we want to model different payment instruments (e.g. Credit Card and PayPal) for different kinds of account (e.g. Personal and Business). The Bridge Pattern lets us separate payment methods from accounts, giving us flexible combinations without having to create a class for every combination of account and payment method.

// Implementation: interface for payment methods
interface PaymentMethod {
    public function processPayment($amount);
}

// Concrete implementation: Credit Card
class CreditCard implements PaymentMethod {
    public function processPayment($amount) {
        echo "Payment of $amount processed via Credit Card.\n";
    }
}

// Concrete implementation: PayPal
class PayPal implements PaymentMethod {
    public function processPayment($amount) {
        echo "Payment of $amount processed via PayPal.\n";
    }
}

// Abstraction: the Account
abstract class Account {
    protected $paymentMethod;

    public function __construct(PaymentMethod $paymentMethod) {
        $this->paymentMethod = $paymentMethod;
    }

    abstract public function makePayment($amount);
}

// Refined Abstraction: Personal Account
class PersonalAccount extends Account {
    public function makePayment($amount) {
        echo "Personal Account: ";
        $this->paymentMethod->processPayment($amount);
    }
}

// Refined Abstraction: Business Account
class BusinessAccount extends Account {
    public function makePayment($amount) {
        echo "Business Account: ";
        $this->paymentMethod->processPayment($amount);
    }
}

// Usage
$personalAccountWithCreditCard = new PersonalAccount(new CreditCard());
$personalAccountWithCreditCard->makePayment(100);  // Output: Personal Account: Payment of 100 processed via Credit Card.

$businessAccountWithPayPal = new BusinessAccount(new PayPal());
$businessAccountWithPayPal->makePayment(250);  // Output: Business Account: Payment of 250 processed via PayPal.

BACK TO THE GUIDE

Copiato