Structural Design Patterns in PHP: Real-World Examples

Sohag HasanSohag Hasan
6 min read

Understanding Structural Design Patterns in PHP: A Practical Guide

Introduction ๐ŸŽฏ

Ever walked into a LEGO store? You'll see individual bricks that can be assembled into amazing structures. Structural design patterns in PHP work similarly โ€“ they're blueprints that help us compose objects and classes into larger, more functional structures. Just as LEGO pieces need to fit perfectly together, these patterns ensure our code components work harmoniously.

Why Should You Care? ๐Ÿค”

Imagine you're building a house. You wouldn't start nailing boards together randomly โ€“ you'd follow architectural plans. Similarly, structural design patterns are your architectural plans for writing better code. They help you:

  • Solve common design problems with proven solutions

  • Write more maintainable and flexible code

  • Communicate better with other developers

  • Save time by not reinventing the wheel

What We'll Cover ๐Ÿ“š

In this comprehensive guide, we'll explore five essential structural patterns:

  1. Adapter Pattern - Like a universal power adapter that lets you charge your phone anywhere

  2. Bridge Pattern - Think of a TV remote that works with any TV brand

  3. Composite Pattern - Similar to how organizations structure their employees

  4. Decorator Pattern - Like customizing your coffee with extra toppings

  5. Facade Pattern - Think of a car dashboard simplifying complex engine operations

For each pattern, we'll:

  • Explain the concept using real-world analogies

  • Provide practical PHP code examples

  • Share everyday situations where you might use them

  • Discuss best practices and common pitfalls

Who Is This Guide For? ๐Ÿ‘ฅ

  • PHP developers looking to level up their architecture skills

  • Team leads wanting to implement better coding standards

  • Anyone interested in writing more maintainable PHP code

  • Developers preparing for technical interviews

Prerequisites ๐Ÿ”

To get the most out of this guide, you should:

  • Have basic PHP knowledge

  • Understand object-oriented programming concepts

  • Be familiar with interfaces and inheritance

Let's dive in and explore how these patterns can transform your code from good to great! ๐Ÿš€

1. Adapter Pattern ๐Ÿ”Œ

Real-Life Examples:

  1. Travel Power Adapter

    • Your US laptop charger (120V) needs an adapter to work in Europe (230V)

    • The adapter converts without changing your device or the power outlet

  2. Language Translator

    • A translator adapts one language to another

    • The original message remains the same, just converted to a different format

  3. Card Reader

    • SD card adapter that lets you use micro-SD cards in regular SD slots
// Language Translator Example
interface EnglishSpeaker {
    public function speakEnglish();
}

interface FrenchSpeaker {
    public function speakFrench();
}

class FrenchPerson implements FrenchSpeaker {
    public function speakFrench() {
        return "Bonjour!";
    }
}

class LanguageAdapter implements EnglishSpeaker {
    private $frenchSpeaker;

    public function __construct(FrenchSpeaker $speaker) {
        $this->frenchSpeaker = $speaker;
    }

    public function speakEnglish() {
        $french = $this->frenchSpeaker->speakFrench();
        return ($french === "Bonjour!") ? "Hello!" : "Translation not found";
    }
}

2. Bridge Pattern ๐ŸŒ‰

Real-Life Examples:

  1. TV Remote Control

    • Same remote works with different TV brands

    • Volume up/down works consistently across brands

  2. Car Controls

    • Steering wheel, pedals, gear shift work the same way across different car models

    • Different engines (electric, petrol) but same driving interface

  3. Paint Brush and Colors

    • Same brush can be used with different colors

    • Different brush types can use the same colors

// Paint and Brush Example
interface Color {
    public function getColor();
}

interface Brush {
    public function paint();
}

class RedColor implements Color {
    public function getColor() {
        return "red";
    }
}

class WatercolorBrush implements Brush {
    protected $color;

    public function __construct(Color $color) {
        $this->color = $color;
    }

    public function paint() {
        return "Painting with " . $this->color->getColor() . " watercolor";
    }
}

3. Composite Pattern ๐ŸŒณ

Real-Life Examples:

  1. Family Tree

    • Each family member can be an individual or have their own family

    • You can get information about a single person or an entire branch

  2. File System

    • Files and folders are treated the same way

    • A folder can contain files or other folders

  3. Shopping Cart with Packages

    • Individual items and bundles are treated as purchasable items

    • Calculate total price regardless of structure

// File System Example
interface FileSystem {
    public function getSize(): int;
    public function getName(): string;
}

class File implements FileSystem {
    private $name;
    private $size;

    public function __construct(string $name, int $size) {
        $this->name = $name;
        $this->size = $size;
    }

    public function getSize(): int {
        return $this->size;
    }

    public function getName(): string {
        return $this->name;
    }
}

class Folder implements FileSystem {
    private $name;
    private $contents = [];

    public function add(FileSystem $item) {
        $this->contents[] = $item;
    }

    public function getSize(): int {
        return array_sum(array_map(fn($item) => $item->getSize(), $this->contents));
    }
}

4. Decorator Pattern ๐ŸŽ€

Real-Life Examples:

  1. Coffee Customization

    • Start with basic coffee

    • Add milk, sugar, whipped cream, etc.

    • Each addition changes price and description

  2. Pizza Toppings

    • Basic pizza crust

    • Add toppings: cheese, pepperoni, mushrooms

    • Each topping affects final price

  3. Car Customization

    • Base model car

    • Add features: AC, leather seats, navigation

    • Each feature adds to total cost

// Pizza Example
interface Pizza {
    public function getDescription(): string;
    public function getCost(): float;
}

class BasicPizza implements Pizza {
    public function getDescription(): string {
        return "Basic pizza with tomato sauce";
    }

    public function getCost(): float {
        return 8.00;
    }
}

class CheeseDecorator implements Pizza {
    private $pizza;

    public function __construct(Pizza $pizza) {
        $this->pizza = $pizza;
    }

    public function getDescription(): string {
        return $this->pizza->getDescription() . ", mozzarella cheese";
    }

    public function getCost(): float {
        return $this->pizza->getCost() + 2.50;
    }
}

5. Facade Pattern ๐Ÿข

Real-Life Examples:

  1. Restaurant Waiter

    • Waiter provides simple interface to complex kitchen system

    • You don't interact with cooks, dishwashers, or suppliers

    • Just order and receive food

  2. Hotel Reception

    • Single point of contact for various services

    • Handles room service, housekeeping, maintenance

    • You don't need to know internal operations

  3. Car Dashboard

    • Simple interface for complex car systems

    • Don't need to understand engine, electrical systems

// Restaurant Facade Example
class Kitchen {
    public function cookFood(string $dish) {
        return "Cooking $dish";
    }
}

class Inventory {
    public function checkIngredients(string $dish): bool {
        return true; // Simplified
    }
}

class Billing {
    public function generateBill(string $dish): float {
        return 15.00; // Simplified
    }
}

class WaiterFacade {
    private $kitchen;
    private $inventory;
    private $billing;

    public function __construct() {
        $this->kitchen = new Kitchen();
        $this->inventory = new Inventory();
        $this->billing = new Billing();
    }

    public function orderFood(string $dish): array {
        $result = [];

        if ($this->inventory->checkIngredients($dish)) {
            $result['status'] = $this->kitchen->cookFood($dish);
            $result['bill'] = $this->billing->generateBill($dish);
        }

        return $result;
    }
}

// Usage
$waiter = new WaiterFacade();
$order = $waiter->orderFood("Pasta");

Pattern Selection Guide ๐ŸŽฏ

Choose based on your real-world scenario:

  1. Use Adapter when:

    • Working with incompatible interfaces

    • Integrating third-party services

    • Need to convert data formats

  2. Use Bridge when:

    • Need to switch implementations easily

    • Have cross-platform requirements

    • Want to separate interface from implementation

  3. Use Composite when:

    • Dealing with tree structures

    • Need to treat groups and individuals uniformly

    • Building hierarchical menus

  4. Use Decorator when:

    • Need to add features dynamically

    • Want flexible alternative to subclassing

    • Building customizable products

  5. Use Facade when:

    • Simplifying complex systems

    • Creating unified API

    • Reducing system coupling

Remember: The best pattern is the one that solves your specific problem while keeping code maintainable and understandable. Don't force patterns where they're not needed!

Would you like to see more specific examples or explore any pattern in more detail?

0
Subscribe to my newsletter

Read articles from Sohag Hasan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sohag Hasan
Sohag Hasan

WhoAmI => notes.sohag.pro/author