Have you ever needed to create several related objects, like UI components for different themes (light/dark), databases, or API drivers? If yes, you probably noticed that keeping your code flexible and easy to maintain can be hard. This is where the Abstract Factory pattern helps.
In this post, I will show you how to use Abstract Factory in PHP, with simple examples and tips from real experience.
The Problem
Imagine you are building a user interface that can be shown in light or dark theme. Each theme has its own button and checkbox. Without a pattern, your code might look like this:
if ($theme === 'dark') {
$button = new DarkButton();
$checkbox = new DarkCheckbox();
} else {
$button = new LightButton();
$checkbox = new LightCheckbox();
}
This works, but it is hard to maintain. If you want to add a new theme, you need to change your code in many places. Also, you might mix components from different themes by mistake.
Solution: Abstract Factory
The Abstract Factory pattern solves this problem by creating an interface to produce families of related objects, without depending on their concrete classes.
Define Interfaces
interface Button {
public function render(): void;
}
interface Checkbox {
public function render(): void;
}
Implement the concrete class
class LightButton implements Button {
public function render(): void {
echo "Light Button<br>";
}
}
class DarkButton implements Button {
public function render(): void {
echo "Dark Button<br>";
}
}
class LightCheckbox implements Checkbox {
public function render(): void {
echo "Light Checkbox<br>";
}
}
class DarkCheckbox implements Checkbox {
public function render(): void {
echo "Dark Checkbox<br>";
}
}
Create the abstract factory
interface UIFactory {
public function createButton(): Button;
public function createCheckbox(): Checkbox;
}
Implement concrete factories
class LightFactory implements UIFactory {
public function createButton(): Button {
return new LightButton();
}
public function createCheckbox(): Checkbox {
return new LightCheckbox();
}
}
class DarkFactory implements UIFactory {
public function createButton(): Button {
return new DarkButton();
}
public function createCheckbox(): Checkbox {
return new DarkCheckbox();
}
}
How to use implementation
function renderUI(UIFactory $factory) {
$button = $factory->createButton();
$checkbox = $factory->createCheckbox();
$button->render();
$checkbox->render();
}
$theme = 'dark';
$factory = $theme === 'dark' ? new DarkFactory() : new LightFactory();
renderUI($factory);
Advantages
- Decoupling: The client code does not need to know which concrete classes are used.
- Consistency: It makes sure all objects created belong to the same family(theme)
- Easy to extend: To add a new theme, just create a new factory and new concrete products.
Conclusion
Abstract Factory can look complex at first, but it is an elegant solution for real problems of maintenance and scalability. In big projects, it really helps to organize your code.
Top comments (0)