Creational Design Patterns in PHP

As experienced software developers, we often encounter scenarios where we need to create objects in a way that’s both flexible and efficient. Creational design patterns offer elegant solutions to address these challenges. In this article, we’ll explore five essential creational design patterns, all illustrated within the context of a simple e-commerce application.

1. Factory Method

Imagine an e-commerce system with various product types, like books, and games. The Factory Method pattern provides a way to create products without specifying their exact classes. Let’s create a PHP example:

interface Product {
    public function getDescription(): string;
}

class Book implements Product {
    public function getDescription(): string {
        return "This is a book.";
    }
}

class Game implements Product {
    public function getDescription(): string {
        return "This is a game.";
    }
}

class ProductFactory {
    public function createProduct(string $type): Product {
        switch ($type) {
            case 'book':
                return new Book();
            case 'game':
                return new Game();
            default:
                throw new InvalidArgumentException("Invalid product type");
        }
    }
}

With the ProductFactory, we can create different product objects easily.

2. Abstract Factory

The Abstract Factory pattern extends the Factory Method by providing a family of related product objects. In our e-commerce example, we can have a ProductFactory for each product category (e.g., Books and Games) to ensure that related products are created consistently.

// Game type Product
class Game implements Product {
    public function getDescription(): string {
        return "This is a Game.";
    }
}

// Abstract Factory Interface
interface ProductFactory {
    public function createProduct(): Product;
}

// Implementing the Interface
class GamesFactory implements ProductFactory {
    public function createProduct(): Product {
        return new Game();
    }
}

// Logic wrapped in a method
function createAndDescribeProduct(ProductFactory $factory) {
    $product = $factory->createProduct();
    echo $product->getDescription() . PHP_EOL;
}

// Using the Abstract Factory to create and describe products from different types
createAndDescribeProduct(new GamesFactory());
createAndDescribeProduct(new BooksFactory());

3. Singleton

In e-commerce, we might need a Singleton to manage the shopping cart. This ensures that only one instance of the cart exists throughout the application’s lifetime. Here’s a PHP example:

class ShoppingCart {
    private static ?ShoppingCart $instance = null;

    private function __construct() {}

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

4. Builder

The Builder pattern is handy when creating complex objects step by step. In our e-commerce application, consider constructing a shopping cart with various product details.

class ShoppingCartBuilder {
    private ShoppingCart $cart;

    public function __construct() {
        $this->cart = new ShoppingCart();
    }

    public function addProduct(Product $product): void {
        // Logic to add the product to the cart
    }

    public function build(): ShoppingCart {
        return $this->cart;
    }
}

Here’s how you might use this builder in your code:

// Create a new ShoppingCartBuilder
$cartBuilder = new ShoppingCartBuilder();

// Add products to the cart
$cartBuilder->addProduct(new Book());
$cartBuilder->addProduct(new Electronics());

// Build the final ShoppingCart
$shoppingCart = $cartBuilder->build();

5. Prototype

The Prototype pattern allows us to clone objects, useful for creating new instances based on an existing one. In our e-commerce system, you could clone a shopping cart for a new user starting with a predefined cart.

class ShoppingCartCloneable implements Cloneable {
    private ShoppingCart $cart;

    public function __clone() {
        $this->cart = clone $this->cart;
    }
}

So, when a new user arrives, we clone the prototypeCart to create a fresh cart for the new user.

// Create a prototype ShoppingCart with some initial products
$prototypeCart = new ShoppingCart();
$prototypeCart->addProduct(new Delivery());
$prototypeCart->addProduct(new AdditionalDefaultProduct());

// Clone the prototypeCart to create a new cart for a new user
$newUserCart = clone $prototypeCart;

// Add additional products to the new user's cart
$newUserCart->addProduct(new Game());

// Display the contents of the new user's cart
$newUserCart->displayContents();

Conclusion

In conclusion, creational design patterns in PHP provide us with powerful tools to manage object creation in a structured and efficient way. By applying these patterns, we can build flexible and maintainable software, even in complex domains like e-commerce.

Keep these patterns in your toolbox, and use them wisely to streamline your PHP projects. Happy coding!

Leave a Comment