PHP - Design Patterns

Hello, aspiring programmers! I'm thrilled to embark on this journey with you into the fascinating world of PHP design patterns. As your guide, I'll use my years of teaching experience to break down these concepts in a way that's easy to understand, even if you've never written a line of code before. So, let's dive in!

PHP - Design Patterns

What are Design Patterns?

Before we delve into specific patterns, let's understand what design patterns are. Imagine you're building a house. You wouldn't start from scratch every time, right? You'd use tried-and-tested blueprints. Design patterns are like these blueprints for software development. They're reusable solutions to common problems that developers face.

Now, let's explore some key design patterns in PHP.

Singleton Pattern

What is the Singleton Pattern?

The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. It's like having a single, special key that opens a very important door, and everyone who needs to use that door must use that same key.

When to Use It?

Use the Singleton pattern when:

  • Exactly one instance of a class is needed
  • You want to control access to a shared resource

Code Example

class Database {
    private static $instance = null;
    private $connection;

    private function __construct() {
        $this->connection = new PDO("mysql:host=localhost;dbname=mydb", "username", "password");
    }

    public static function getInstance() {
        if (self::$instance == null) {
            self::$instance = new Database();
        }
        return self::$instance;
    }

    public function query($sql) {
        return $this->connection->query($sql);
    }
}

// Usage
$db1 = Database::getInstance();
$db2 = Database::getInstance();

// $db1 and $db2 are the same instance

In this example, we create a Database class that can only have one instance. The getInstance() method checks if an instance already exists. If not, it creates one; otherwise, it returns the existing instance.

Factory Pattern

What is the Factory Pattern?

The Factory pattern is like a manufacturing plant. Instead of creating objects directly, we use a factory method to create them. This allows for flexibility in object creation and decouples the creation logic from the main code.

When to Use It?

Use the Factory pattern when:

  • The type of objects you need to create is determined at runtime
  • You want to centralize the creation logic of related objects

Code Example

interface Animal {
    public function speak();
}

class Dog implements Animal {
    public function speak() {
        return "Woof!";
    }
}

class Cat implements Animal {
    public function speak() {
        return "Meow!";
    }
}

class AnimalFactory {
    public static function createAnimal($type) {
        switch($type) {
            case 'dog':
                return new Dog();
            case 'cat':
                return new Cat();
            default:
                throw new Exception("Invalid animal type");
        }
    }
}

// Usage
$dog = AnimalFactory::createAnimal('dog');
echo $dog->speak(); // Output: Woof!

$cat = AnimalFactory::createAnimal('cat');
echo $cat->speak(); // Output: Meow!

Here, we have an AnimalFactory that creates different types of animals. This allows us to add new animal types easily without changing the client code.

Strategy Pattern

What is the Strategy Pattern?

The Strategy pattern allows you to define a family of algorithms, encapsulate each one, and make them interchangeable. It's like having different routes to reach a destination and being able to switch between them based on traffic conditions.

When to Use It?

Use the Strategy pattern when:

  • You have multiple algorithms for a specific task
  • You want to be able to switch between these algorithms dynamically

Code Example

interface SortStrategy {
    public function sort(array $data): array;
}

class BubbleSort implements SortStrategy {
    public function sort(array $data): array {
        // Bubble sort implementation
        return $data;
    }
}

class QuickSort implements SortStrategy {
    public function sort(array $data): array {
        // Quick sort implementation
        return $data;
    }
}

class Sorter {
    private $sortStrategy;

    public function __construct(SortStrategy $sortStrategy) {
        $this->sortStrategy = $sortStrategy;
    }

    public function sort(array $data): array {
        return $this->sortStrategy->sort($data);
    }
}

// Usage
$data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5];

$bubbleSorter = new Sorter(new BubbleSort());
$bubbleSorted = $bubbleSorter->sort($data);

$quickSorter = new Sorter(new QuickSort());
$quickSorted = $quickSorter->sort($data);

In this example, we can easily switch between different sorting algorithms without changing the client code.

MVC Design Pattern

What is the MVC Pattern?

MVC stands for Model-View-Controller. It's a architectural pattern that separates an application into three main components:

  • Model: Manages data and business logic
  • View: Handles the display of data
  • Controller: Acts as an intermediary between Model and View

When to Use It?

Use the MVC pattern when:

  • You want to separate concerns in your application
  • You need to make your code more maintainable and scalable

Code Example

// Model
class UserModel {
    public function getUsers() {
        // Code to fetch users from database
        return ['Alice', 'Bob', 'Charlie'];
    }
}

// View
class UserView {
    public function showUsers($users) {
        echo "<ul>";
        foreach ($users as $user) {
            echo "<li>$user</li>";
        }
        echo "</ul>";
    }
}

// Controller
class UserController {
    private $model;
    private $view;

    public function __construct() {
        $this->model = new UserModel();
        $this->view = new UserView();
    }

    public function showUsers() {
        $users = $this->model->getUsers();
        $this->view->showUsers($users);
    }
}

// Usage
$controller = new UserController();
$controller->showUsers();

This example demonstrates a simple MVC structure. The Controller fetches data from the Model and passes it to the View for display.

Summary of Design Patterns

Here's a quick reference table of the design patterns we've discussed:

Pattern Purpose Key Benefit
Singleton Ensure a class has only one instance Controls access to shared resources
Factory Create objects without specifying their exact class Flexibility in object creation
Strategy Define a family of interchangeable algorithms Allows switching algorithms at runtime
MVC Separate application logic into three components Improves maintainability and scalability

Remember, these patterns are tools in your programming toolkit. Like any tool, they're most effective when used in the right situation. As you gain more experience, you'll develop an intuition for when and how to apply them.

I hope this tutorial has given you a solid foundation in PHP design patterns. Keep practicing, stay curious, and happy coding!

Credits: Image by storyset