Creational Design Patterns in PHP: Crafting Objects with Elegance

WhoAmI => notes.sohag.pro/author
Search for a command to run...

WhoAmI => notes.sohag.pro/author
No comments yet. Be the first to comment.
Distributed Caching Architectures in Laravel Introduction Imagine you're preparing breakfast every morning. Instead of taking out all the ingredients from different cabinets each time, you keep frequently used items like coffee, sugar, and bread on t...
I had a solid list of reasons my life wasn't moving faster, until a grainy old lecture pointed out the one name missing from it

The finale isn't a victory lap. It's the story of the control I shipped that did nothing, the footgun still sitting in my demo, and the handful of things I'd keep exactly as they are.

How do you hold a large payment for a second pair of eyes without ever letting the unapproved money touch a balance, and how do you stream that decision to the outside world without standing up a broker?

How do you show the total under a parent account when the whole system refuses to store a balance? A recursive query, a trigger that refuses to draw a circle, and a rule about what actually has to sum to zero.

Every time I wanted to change an FX rate I had to edit a file on the server and restart the app. So I moved rates and markup into a live admin API, and then audited it hard enough to find the bug that quietly undid the whole thing.

Design patterns are like the secret recipes of software development - they provide proven solutions to common design problems. Creational design patterns specifically focus on object creation mechanisms, making object instantiation more flexible and efficient. Think of them as intelligent object factories that know exactly how to create objects in the most optimal way.
Imagine a campfire where only one fire can exist at a time. The Singleton pattern ensures that a class has only one instance and provides a global point of access to it.
In a coffee shop, you want only one global configuration manager for your coffee machines. No matter which barista accesses it, they get the same configuration.
class CoffeeMachineConfig {
private static ?self $instance = null;
private array $settings = [
'water_temperature' => 92,
'beans_grind_level' => 'medium'
];
private function __construct() {}
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getSettings(): array {
return $this->settings;
}
}
// Usage
$config1 = CoffeeMachineConfig::getInstance();
$config2 = CoffeeMachineConfig::getInstance();
// $config1 and $config2 are the SAME instance
Think of a factory method as a customizable object creation process. Instead of directly instantiating objects, you define an interface for creating objects but let subclasses decide which class to instantiate.
Imagine a transportation company that creates different types of vehicles based on specific requirements.
interface Vehicle {
public function move(): string;
}
class Car implements Vehicle {
public function move(): string {
return "Driving on roads";
}
}
class Bicycle implements Vehicle {
public function move(): string {
return "Pedaling through bike lanes";
}
}
abstract class VehicleFactory {
abstract public function createVehicle(): Vehicle;
public function orderVehicle(): string {
$vehicle = $this->createVehicle();
return $vehicle->move();
}
}
class CarFactory extends VehicleFactory {
public function createVehicle(): Vehicle {
return new Car();
}
}
class BicycleFactory extends VehicleFactory {
public function createVehicle(): Vehicle {
return new Bicycle();
}
}
// Usage
$carFactory = new CarFactory();
echo $carFactory->orderVehicle(); // Outputs: Driving on roads
$bicycleFactory = new BicycleFactory();
echo $bicycleFactory->orderVehicle(); // Outputs: Pedaling through bike lanes
Consider an abstract factory as a super-factory that creates families of related objects without specifying their concrete classes.
Imagine creating different UI themes for multiple platforms, ensuring consistency across design elements.
interface Button {
public function render(): string;
}
interface Checkbox {
public function render(): string;
}
class WindowsButton implements Button {
public function render(): string {
return "Windows-style button";
}
}
class MacButton implements Button {
public function render(): string {
return "Mac-style button";
}
}
class WindowsCheckbox implements Checkbox {
public function render(): string {
return "Windows-style checkbox";
}
}
class MacCheckbox implements Checkbox {
public function render(): string {
return "Mac-style checkbox";
}
}
interface UIFactory {
public function createButton(): Button;
public function createCheckbox(): Checkbox;
}
class WindowsUIFactory implements UIFactory {
public function createButton(): Button {
return new WindowsButton();
}
public function createCheckbox(): Checkbox {
return new WindowsCheckbox();
}
}
class MacUIFactory implements UIFactory {
public function createButton(): Button {
return new MacButton();
}
public function createCheckbox(): Checkbox {
return new MacCheckbox();
}
}
Think of the Builder pattern as a step-by-step object construction process, allowing you to create complex objects with numerous configuration options.
Creating a computer with multiple configurable components becomes straightforward.
class Computer {
private ?string $cpu = null;
private ?string $ram = null;
private ?string $storage = null;
public function setCPU(string $cpu): self {
$this->cpu = $cpu;
return $this;
}
public function setRAM(string $ram): self {
$this->ram = $ram;
return $this;
}
public function setStorage(string $storage): self {
$this->storage = $storage;
return $this;
}
public function __toString(): string {
return "Computer: CPU={$this->cpu}, RAM={$this->ram}, Storage={$this->storage}";
}
}
class ComputerBuilder {
private Computer $computer;
public function __construct() {
$this->computer = new Computer();
}
public function configureCoder(): self {
$this->computer
->setCPU('High-Performance Intel i9')
->setRAM('32GB')
->setStorage('1TB SSD');
return $this;
}
public function configureGamer(): self {
$this->computer
->setCPU('AMD Ryzen 7')
->setRAM('64GB')
->setStorage('2TB NVMe');
return $this;
}
public function getComputer(): Computer {
return $this->computer;
}
}
// Usage
$builder = new ComputerBuilder();
$coderComputer = $builder->configureCoder()->getComputer();
$gamerComputer = $builder->configureGamer()->getComputer();
echo $coderComputer; // Detailed coder computer configuration
echo $gamerComputer; // Detailed gamer computer configuration
The Prototype pattern allows you to copy existing objects without making your code dependent on their classes.
Creating multiple website configurations by cloning a base template.
interface WebsitePrototype {
public function clone(): self;
public function getConfiguration(): array;
}
class WebsiteTemplate implements WebsitePrototype {
private array $configuration;
public function __construct(array $config) {
$this->configuration = $config;
}
public function clone(): self {
return new self($this->configuration);
}
public function getConfiguration(): array {
return $this->configuration;
}
}
// Usage
$baseTemplate = new WebsiteTemplate([
'theme' => 'modern',
'color_scheme' => 'dark',
'layout' => 'responsive'
]);
$newWebsite1 = $baseTemplate->clone();
$newWebsite2 = $baseTemplate->clone();
Creational design patterns provide powerful strategies for object creation, offering flexibility, reusability, and maintainability in your PHP applications. By understanding and implementing these patterns, you transform from writing code to designing elegant software architectures.
Singleton: One instance, global access
Factory Method: Flexible object creation
Abstract Factory: Creating object families
Builder: Step-by-step object construction
Prototype: Cloning objects efficiently
Remember, design patterns are tools, not rules. Use them judiciously and always consider your specific use case.
Happy Coding! 🚀