Understanding Behavioral Design Patterns in PHP: A Beginner's Guide

Sohag HasanSohag Hasan
7 min read

Behavioral Design Patterns in PHP

Introduction ๐ŸŽฏ

Imagine conducting an orchestra where each musician knows exactly when to play their part. Behavioral design patterns are like musical scores - they define how objects communicate and interact with each other. Just as musicians follow a conductor, these patterns establish clear rules for object collaboration.

Why Learn Behavioral Patterns? ๐Ÿค”

Think of behavioral patterns as the "social skills" of your code. They help:

  • Manage communication between objects

  • Handle complex operations smoothly

  • Make code more flexible and maintainable

  • Solve common programming challenges

Let's explore these patterns using everyday examples that you can relate to!

1. Observer Pattern ๐Ÿ‘€

Real-Life Example:

  • YouTube subscribers getting notifications

  • Newsletter subscribers receiving emails

  • Social media followers getting updates

Think of it as a newspaper subscription - subscribers automatically receive new editions when published.

interface Subject {
    public function attach(Observer $observer);
    public function detach(Observer $observer);
    public function notify();
}

interface Observer {
    public function update(string $message);
}

class YouTubeChannel implements Subject {
    private $observers = [];
    private $latestVideo;

    public function attach(Observer $observer) {
        $this->observers[] = $observer;
    }

    public function detach(Observer $observer) {
        $key = array_search($observer, $this->observers, true);
        if ($key !== false) {
            unset($this->observers[$key]);
        }
    }

    public function notify() {
        foreach ($this->observers as $observer) {
            $observer->update($this->latestVideo);
        }
    }

    public function uploadVideo(string $title) {
        $this->latestVideo = $title;
        $this->notify();
    }
}

class Subscriber implements Observer {
    private $name;

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

    public function update(string $message) {
        echo "$this->name received notification: New video '$message'\n";
    }
}

// Usage
$channel = new YouTubeChannel();
$bob = new Subscriber("Bob");
$alice = new Subscriber("Alice");

$channel->attach($bob);
$channel->attach($alice);
$channel->uploadVideo("PHP Design Patterns");

2. Strategy Pattern ๐ŸŽฏ

Real-Life Example:

  • Different payment methods (Credit Card, PayPal, Cash)

  • Various navigation routes (Walking, Driving, Public Transit)

  • Different sorting algorithms

Think of it as choosing transportation to work - you can take a bus, drive, or cycle, but the end goal is the same.

interface PaymentStrategy {
    public function pay(float $amount): string;
}

class CreditCardPayment implements PaymentStrategy {
    private $cardNumber;

    public function __construct(string $cardNumber) {
        $this->cardNumber = $cardNumber;
    }

    public function pay(float $amount): string {
        return "Paid $amount using Credit Card ending with " . 
               substr($this->cardNumber, -4);
    }
}

class PayPalPayment implements PaymentStrategy {
    private $email;

    public function __construct(string $email) {
        $this->email = $email;
    }

    public function pay(float $amount): string {
        return "Paid $amount using PayPal account: $this->email";
    }
}

class ShoppingCart {
    private $paymentStrategy;

    public function setPaymentStrategy(PaymentStrategy $strategy) {
        $this->paymentStrategy = $strategy;
    }

    public function checkout(float $amount) {
        return $this->paymentStrategy->pay($amount);
    }
}

3. Command Pattern ๐ŸŽฎ

Real-Life Example:

  • Remote control buttons

  • Restaurant orders to kitchen

  • Queue management systems

Think of it as a restaurant where waiters (commands) take orders to the kitchen without knowing how the food is prepared.

interface Command {
    public function execute();
}

class Light {
    public function turnOn(): string {
        return "Light is on";
    }

    public function turnOff(): string {
        return "Light is off";
    }
}

class LightOnCommand implements Command {
    private $light;

    public function __construct(Light $light) {
        $this->light = $light;
    }

    public function execute() {
        return $this->light->turnOn();
    }
}

class RemoteControl {
    private $command;

    public function setCommand(Command $command) {
        $this->command = $command;
    }

    public function pressButton() {
        return $this->command->execute();
    }
}

4. Iterator Pattern ๐Ÿ”„

Real-Life Example:

  • Flipping through a book

  • Browsing TV channels

  • Going through a playlist

Think of it as reading a book - you can move forward, backward, or jump to specific pages.

class Playlist implements Iterator {
    private $songs = [];
    private $position = 0;

    public function addSong(string $song) {
        $this->songs[] = $song;
    }

    public function current() {
        return $this->songs[$this->position];
    }

    public function next() {
        $this->position++;
    }

    public function key() {
        return $this->position;
    }

    public function valid() {
        return isset($this->songs[$this->position]);
    }

    public function rewind() {
        $this->position = 0;
    }
}

5. State Pattern ๐Ÿ”„

Real-Life Examples:

  1. Vending Machine

    • Different states: Ready, Processing, Out of Stock, Maintenance

    • Each state determines available actions

  2. Traffic Light

    • States: Red, Yellow, Green

    • Each state has specific behavior and rules for transition

  3. Order Status

    • States: New, Confirmed, Processing, Shipped, Delivered

    • Different actions available in each state

interface OrderState {
    public function processOrder(Order $order): string;
    public function cancelOrder(Order $order): string;
    public function getStatus(): string;
}

class NewOrderState implements OrderState {
    public function processOrder(Order $order): string {
        $order->setState(new ProcessingState());
        return "Order is now being processed";
    }

    public function cancelOrder(Order $order): string {
        $order->setState(new CancelledState());
        return "Order cancelled successfully";
    }

    public function getStatus(): string {
        return "New";
    }
}

class ProcessingState implements OrderState {
    public function processOrder(Order $order): string {
        $order->setState(new ShippedState());
        return "Order is now shipped";
    }

    public function cancelOrder(Order $order): string {
        return "Cannot cancel order in processing state";
    }

    public function getStatus(): string {
        return "Processing";
    }
}

class Order {
    private $state;
    private $orderNumber;

    public function __construct(string $orderNumber) {
        $this->orderNumber = $orderNumber;
        $this->state = new NewOrderState();
    }

    public function setState(OrderState $state) {
        $this->state = $state;
    }

    public function processOrder(): string {
        return $this->state->processOrder($this);
    }

    public function cancelOrder(): string {
        return $this->state->cancelOrder($this);
    }

    public function getStatus(): string {
        return $this->state->getStatus();
    }
}

// Usage
$order = new Order("ORD-123");
echo $order->getStatus(); // "New"
echo $order->processOrder(); // "Order is now being processed"
echo $order->cancelOrder(); // "Cannot cancel order in processing state"

6. Visitor Pattern ๐Ÿ‘ฅ

Real-Life Examples:

  1. Home Inspector

    • Visits different parts of house

    • Performs specific checks for each area

    • Generates report without modifying house

  2. Tax Calculator

    • Different rules for different income types

    • Visits various income sources

    • Calculates without changing income structure

  3. Shopping Cart Discount

    • Different discounts for different product types

    • Visits each item in cart

    • Applies specific rules without modifying products

interface Visitor {
    public function visitBook(Book $book): float;
    public function visitElectronics(Electronics $electronics): float;
}

interface Visitable {
    public function accept(Visitor $visitor): float;
}

class DiscountVisitor implements Visitor {
    public function visitBook(Book $book): float {
        // 10% discount for books
        return $book->getPrice() * 0.90;
    }

    public function visitElectronics(Electronics $electronics): float {
        // 5% discount for electronics
        return $electronics->getPrice() * 0.95;
    }
}

class Book implements Visitable {
    private $price;
    private $title;

    public function __construct(string $title, float $price) {
        $this->title = $title;
        $this->price = $price;
    }

    public function getPrice(): float {
        return $this->price;
    }

    public function accept(Visitor $visitor): float {
        return $visitor->visitBook($this);
    }
}

class Electronics implements Visitable {
    private $price;
    private $name;

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

    public function getPrice(): float {
        return $this->price;
    }

    public function accept(Visitor $visitor): float {
        return $visitor->visitElectronics($this);
    }
}

class ShoppingCart {
    private $items = [];

    public function addItem(Visitable $item) {
        $this->items[] = $item;
    }

    public function calculateTotal(Visitor $visitor): float {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->accept($visitor);
        }
        return $total;
    }
}

// Usage
$cart = new ShoppingCart();
$cart->addItem(new Book("PHP Design Patterns", 50.00));
$cart->addItem(new Electronics("Keyboard", 100.00));

$discountVisitor = new DiscountVisitor();
$totalWithDiscount = $cart->calculateTotal($discountVisitor);
echo "Total after discount: $" . $totalWithDiscount;

Remember:

  • Choose patterns based on your specific needs

  • Start simple and refactor as needed

  • Consider maintenance implications

  • Document your implementation decisions

Best Practices ๐Ÿ“š

  1. Pattern Selection

    • Choose patterns based on specific needs

    • Don't overcomplicate simple solutions

    • Consider maintenance implications

  2. Implementation Tips

    • Keep interfaces simple

    • Follow SOLID principles

    • Write clear documentation

    • Use meaningful names

  3. Common Pitfalls to Avoid

    • Overusing patterns

    • Forcing patterns where they don't fit

    • Neglecting code readability

Topics to Explore Further ๐Ÿ”

  1. Advanced Concepts

    • Event-driven programming

    • Reactive programming

    • Design pattern combinations

  2. Related Technologies

    • Laravel Event System

    • Symfony Event Dispatcher

    • PHP SPL (Standard PHP Library)

  3. Testing Strategies

    • Unit testing patterns

    • Behavior-driven development

    • Test doubles and mocks

Learning Resources ๐Ÿ“–

  1. Books

    • "Head First Design Patterns"

    • "PHP Design Patterns" by Brandon Savage

    • "Clean Code" by Robert C. Martin

  2. Online Resources

    • PHP.net documentation

    • Refactoring Guru

    • PHP The Right Way

  3. Practice Projects

    • Build a simple event system

    • Create a custom iterator

    • Implement a command queue

Conclusion ๐ŸŽฌ

Behavioral patterns are powerful tools in a developer's arsenal. They help create more maintainable and flexible code by defining clear communication patterns between objects. Remember:

  • Start with simple implementations

  • Practice with real-world examples

  • Focus on understanding the core concepts

  • Apply patterns where they make sense

The key is not to memorize patterns but to understand their purposes and appropriate use cases. As you build more projects, you'll naturally start recognizing situations where these patterns can help.

Next Steps

  1. Try implementing each pattern in a small project

  2. Study real-world applications of these patterns

  3. Join PHP communities to discuss and learn

  4. Practice refactoring existing code using patterns

Happy coding! ๐Ÿš€

Would you like me to elaborate on any specific pattern or provide more examples?

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