Creational Patterns: Factory, Builder, Singleton
Control object creation cleanly with creational patterns.
Creational Patterns: Factory, Builder, Singleton is a free PHP Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the PHP Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Controlling creation
Creational patterns decouple what you need from how it is built. When construction is conditional, multi-step, or must be centralized, scattering new across the codebase couples callers to concrete classes. Here we cover Factory (and Factory Method), Builder for complex assembly, and Singleton (with its caveats).
Simple Factory
A simple factory centralizes a creation decision behind one method. Callers ask for a logical type and receive a configured concrete instance, without knowing the class names.
<?php
interface Notifier { public function send(string $msg): string; }
final class EmailNotifier implements Notifier {
public function send(string $m): string { return "email: $m"; }
}
final class SmsNotifier implements Notifier {
public function send(string $m): string { return "sms: $m"; }
}
final class NotifierFactory {
public static function make(string $channel): Notifier {
return match ($channel) {
'email' => new EmailNotifier(),
'sms' => new SmsNotifier(),
default => throw new InvalidArgumentException($channel),
};
}
}
echo NotifierFactory::make('sms')->send('Hi'), PHP_EOL;
Factory Method
The Factory Method pattern defers instantiation to subclasses. The base class defines an algorithm that calls an abstract create(); each subclass decides the concrete product. This is OCP-friendly: add a new creator without editing existing ones.
<?php
abstract class ReportExporter {
abstract protected function create(): string;
public function export(): string {
return 'Exported as ' . $this->create();
}
}
final class PdfExporter extends ReportExporter {
protected function create(): string { return 'PDF'; }
}
final class CsvExporter extends ReportExporter {
protected function create(): string { return 'CSV'; }
}
echo (new PdfExporter())->export(), PHP_EOL;
echo (new CsvExporter())->export(), PHP_EOL;
Abstract Factory
An Abstract Factory produces families of related objects that must be used together (e.g. a MySqlConnection + MySqlQueryBuilder). It guarantees the parts are compatible because one factory yields the whole set. Use it when a product family must stay internally consistent.
Builder: the problem
When an object needs many optional parameters, constructors explode into unreadable positional argument lists or telescoping overloads PHP doesn't even have. The Builder pattern assembles a complex object step by step, returning a finished, valid product at the end.
Fluent Builder
A fluent builder returns $this from each setter, then a build() produces the immutable result. This keeps the product itself simple and shifts assembly logic into the builder.
<?php
final class Query {
public function __construct(public readonly string $sql) {}
}
final class QueryBuilder {
private string $table = '';
private array $where = [];
public function from(string $t): self { $this->table = $t; return $this; }
public function where(string $c): self { $this->where[] = $c; return $this; }
public function build(): Query {
$sql = "SELECT * FROM {$this->table}";
if ($this->where) { $sql .= ' WHERE ' . implode(' AND ', $this->where); }
return new Query($sql);
}
}
echo (new QueryBuilder())->from('users')->where('age > 18')->build()->sql, PHP_EOL;
Builder vs named constructors
For simpler cases, PHP 8 named arguments and named constructors (static factory methods) often beat a full builder.
- Use named args when all data is available at once.
- Use a builder when assembly is multi-step, conditional, or reused across call sites.
<?php
final class Money {
private function __construct(public readonly int $cents, public readonly string $cur) {}
public static function fromDollars(float $d): self {
return new self((int) round($d * 100), 'USD');
}
public static function zero(string $cur = 'USD'): self {
return new self(0, $cur);
}
}
var_dump(Money::fromDollars(19.99)->cents); // int(1999)
Singleton
A Singleton guarantees one instance with a global access point. Implement it with a private constructor and a static accessor. It is genuinely useful for things that truly must be unique within a process, but it is also the most abused pattern.
<?php
final class Config {
private static ?self $instance = null;
private array $data = ['debug' => true];
private function __construct() {}
public static function instance(): self {
return self::$instance ??= new self();
}
public function get(string $k): mixed { return $this->data[$k] ?? null; }
}
var_dump(Config::instance()->get('debug'));
var_dump(Config::instance() === Config::instance()); // true
Why Singleton hurts
Singletons introduce hidden global state: callers reach into Config::instance() instead of declaring the dependency, which breaks testability and parallelism. In modern PHP, prefer registering a single shared instance in a DI container ("single instance" lifecycle) and injecting it. You get one instance without the global coupling.
Choosing the right tool
- Simple/Factory Method: hide concrete classes behind a creation decision.
- Abstract Factory: produce compatible product families.
- Builder: assemble complex objects step by step.
- Singleton: last resort; prefer container-managed shared services.
All four reduce coupling to concrete constructors, making code easier to extend and test.
Registry-based factory
A more dynamic factory looks up creators in a registry, so new types register themselves without editing the factory (true OCP). This is how plugin systems and serializers resolve a type name to a constructor at runtime.
<?php
interface Shape { public function name(): string; }
final class Circle implements Shape { public function name(): string { return 'circle'; } }
final class Square implements Shape { public function name(): string { return 'square'; } }
final class ShapeRegistry {
private array $creators = [];
public function register(string $key, callable $factory): void {
$this->creators[$key] = $factory;
}
public function create(string $key): Shape {
return ($this->creators[$key] ?? throw new InvalidArgumentException($key))();
}
}
$r = new ShapeRegistry();
$r->register('circle', fn() => new Circle());
$r->register('square', fn() => new Square());
echo $r->create('square')->name(), PHP_EOL; // square
Quick Check
Pick the best creational tool for the scenario.
Recap
You controlled object creation with the creational patterns: factories to hide concrete types and choose product families, builders to assemble complex objects fluently, and Singleton with its trade-offs. The modern takeaway: prefer factories and builders freely, but replace Singleton with a DI container's shared-instance lifecycle to keep dependencies explicit and testable.
Frequently asked questions
Is the “Creational Patterns: Factory, Builder, Singleton” lesson free?
Yes — the full text of “Creational Patterns: Factory, Builder, Singleton” is free to read here on the web, and the PHP Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the PHP Academy course, upgrade to CoddyKit PRO.
What will I learn in “Creational Patterns: Factory, Builder, Singleton”?
Control object creation cleanly with creational patterns. You practise PHP Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start PHP Academy?
No prior experience is required. PHP Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Creational Patterns: Factory, Builder, Singleton” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this PHP Academy lesson?
Yes. Every PHP Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- SOLID Principles in Practice
- Creational Patterns: Factory, Builder, Singleton
- Structural Patterns: Adapter, Decorator, Facade
- Behavioral Patterns: Strategy, Observer, Command