Comprehensive Introduction to Design Patterns (45 Types) and Their Principles
This article provides an extensive overview of software design patterns, categorizing 45 patterns into Creational, Structural, and Behavioral groups, describing their intent, applicability, advantages, drawbacks, and offering concrete code examples for several patterns such as Factory, Singleton, Builder, and Proxy.
This article presents a thorough guide to design patterns, recommending the "Design Patterns for Humans" Chinese version as a companion and linking to a full GitHub repository.
Types of Design Patterns
There are 23 core patterns, divided into three major categories:
Creational Patterns – hide object creation logic (Factory, Abstract Factory, Singleton, Builder, Prototype, Object Pool, Multiton, Static Factory).
Structural Patterns – focus on class and object composition (Adapter, Bridge, Filter/Criteria, Composite, Decorator, Facade, Flyweight, Proxy, Data Mapper, Dependency Injection, Registry, Fluent Interface).
Behavioral Patterns – emphasize communication between objects (Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Null Object, Strategy, Template, Visitor, Specification).
Six Principles of Design Patterns
Open/Closed Principle : Open for extension, closed for modification.
Liskov Substitution Principle : Subtypes must be substitutable for their base types.
Dependency Inversion Principle : Depend on abstractions, not concretions.
Interface Segregation Principle : Prefer many specific interfaces over a single general one.
Demeter Principle (Least Knowledge) : Each unit should know as little as possible about others.
Composite Reuse Principle : Favor composition over inheritance.
Factory Pattern
The Factory Pattern is one of the most commonly used creational patterns. It creates objects without exposing the creation logic to the client, using a common interface.
Overview
Intent: Define an interface for creating objects, letting subclasses decide which class to instantiate.
Problem Solved: Selecting the appropriate concrete product.
When to Use: When you need to create different objects based on varying conditions.
How it Works: Subclasses implement the factory interface and return abstract products.
Advantages:
Clients need only know the product name.
High extensibility – adding a new product only requires a new factory subclass.
Clients are decoupled from concrete implementations.
Disadvantages: Adding new products increases the number of classes and can raise system complexity.
Typical Use Cases:
Logging frameworks where the logger type is chosen at runtime.
Database access layers that may switch between different DB engines.
Network clients that need to support multiple protocols (e.g., POP3, IMAP, HTTP).
Abstract Factory Pattern
The Abstract Factory Pattern creates families of related objects without specifying their concrete classes.
Overview
Intent: Provide an interface for creating related objects without exposing concrete classes.
Problem Solved: Selecting the appropriate product family.
When to Use: When a system must work with multiple product families but only one at a time.
How it Works: Define multiple products within a family and let concrete factories produce them.
Key Code: A factory aggregates several related product classes.
Advantages: Guarantees that products from the same family are used together.
Disadvantages: Adding new product families can be cumbersome.
Singleton Pattern
The Singleton Pattern ensures a class has only one instance and provides a global access point.
Intent: Guarantee a single instance and provide global access.
Problem Solved: Preventing multiple costly instances.
When to Use: When you need to control the number of instances, such as for configuration or logging.
Key Code: Private constructor and a static accessor method.
Advantages: Reduces memory usage and avoids resource contention.
Disadvantages: Violates the Single Responsibility Principle and can hinder testing.
Builder Pattern
The Builder Pattern constructs complex objects step‑by‑step using a separate Builder class.
Intent: Separate construction of a complex object from its representation.
Problem Solved: Managing the creation of objects with many optional parts.
When to Use: When parts are stable but their combinations vary frequently.
Key Code: Builder creates the product; Director orchestrates the building process.
Prototype Pattern
The Prototype Pattern creates new objects by cloning existing ones, improving performance for costly creations.
Intent: Use a prototype instance to create new objects via cloning.
Problem Solved: Reducing the cost of creating complex objects.
When to Use: When object creation is expensive or when you need many similar objects.
Key Code: Implement Cloneable (Java) or MemberwiseClone (C#) and handle deep copies as needed.
Object Pool Pattern
Object Pool manages a set of initialized objects ready for reuse, improving performance for costly resources.
Sample Code
Pool.php
namespace DesignPatterns\Creational\Pool;
class Pool
{
private $instances = array();
private $class;
public function __construct($class)
{
$this->class = $class;
}
public function get()
{
if (count($this->instances) > 0) {
return array_pop($this->instances);
}
return new $this->class();
}
public function dispose($instance)
{
$this->instances[] = $instance;
}
}Processor.php
namespace DesignPatterns\Creational\Pool;
class Processor
{
private $pool;
private $processing = 0;
private $maxProcesses = 3;
private $waitingQueue = [];
public function __construct(Pool $pool)
{
$this->pool = $pool;
}
public function process($image)
{
if ($this->processing++ < $this->maxProcesses) {
$this->createWorker($image);
} else {
$this->pushToWaitingQueue($image);
}
}
private function createWorker($image)
{
$worker = $this->pool->get();
$worker->run($image, array($this, 'processDone'));
}
public function processDone($worker)
{
$this->processing--;
$this->pool->dispose($worker);
if (count($this->waitingQueue) > 0) {
$this->createWorker($this->popFromWaitingQueue());
}
}
private function pushToWaitingQueue($image)
{
$this->waitingQueue[] = $image;
}
private function popFromWaitingQueue()
{
return array_pop($this->waitingQueue);
}
}Worker.php
namespace DesignPatterns\Creational\Pool;
class Worker
{
public function __construct()
{
// expensive initialization, e.g., creating a thread
}
public function run($image, array $callback)
{
// process $image ...
call_user_func($callback, $this);
}
}Multiton Pattern
Multiton is similar to Singleton but allows a fixed set of named instances (e.g., multiple database connections).
Sample Code
Multiton.php
namespace DesignPatterns\Creational\Multiton;
/**
* Multiton class
*/
class Multiton
{
const INSTANCE_1 = '1';
const INSTANCE_2 = '2';
private static $instances = array();
private function __construct() {}
public static function getInstance($instanceName)
{
if (!array_key_exists($instanceName, self::$instances)) {
self::$instances[$instanceName] = new self();
}
return self::$instances[$instanceName];
}
private function __clone() {}
private function __wakeup() {}
}Static Factory Pattern
Static Factory uses a static method to create related objects, similar to a simple factory but without instantiating the factory itself.
Sample Code
StaticFactory.php
namespace DesignPatterns\Creational\StaticFactory;
class StaticFactory
{
public static function factory($type)
{
$className = __NAMESPACE__ . '\\Format' . ucfirst($type);
if (!class_exists($className)) {
throw new \InvalidArgumentException('Missing format class.');
}
return new $className();
}
}FormatterInterface.php
namespace DesignPatterns\Creational\StaticFactory;
interface FormatterInterface
{
}FormatNumber.php
namespace DesignPatterns\Creational\StaticFactory;
class FormatNumber implements FormatterInterface
{
}Adapter Pattern
Adapter bridges two incompatible interfaces, allowing them to work together.
Intent: Convert one interface to another expected by the client.
Problem Solved: Integrating existing classes into new environments.
Typical Uses: Voltage converters, Java Enumeration to Iterator conversion, cross‑platform compatibility.
Bridge Pattern
Bridge decouples abstraction from implementation so they can vary independently.
Intent: Separate abstraction and implementation.
Problem Solved: Avoiding class explosion when multiple dimensions vary.
Fluent Interface Pattern
Fluent Interface enables method chaining for more readable code, widely used in Laravel query builders.
Sample Code
Sql.php
namespace DesignPatterns\Structural\FluentInterface;
class Sql
{
protected $fields = array();
protected $from = array();
protected $where = array();
public function select(array $fields = array())
{
$this->fields = $fields;
return $this;
}
public function from($table, $alias)
{
$this->from[] = $table . ' AS ' . $alias;
return $this;
}
public function where($condition)
{
$this->where[] = $condition;
return $this;
}
public function getQuery()
{
return 'SELECT ' . implode(',', $this->fields) .
' FROM ' . implode(',', $this->from) .
' WHERE ' . implode(' AND ', $this->where);
}
}Registry Pattern
Registry provides a central storage for frequently used objects, often implemented as a static class.
Sample Code
Registry.php
namespace DesignPatterns\Structural\Registry;
abstract class Registry
{
const LOGGER = 'logger';
protected static $storedValues = array();
public static function set($key, $value)
{
self::$storedValues[$key] = $value;
}
public static function get($key)
{
return self::$storedValues[$key];
}
}Facade Pattern
Facade offers a simplified interface to a complex subsystem.
Sample Code
Facade.php
namespace DesignPatterns\Structural\Facade;
class Facade
{
protected $os;
protected $bios;
public function __construct(BiosInterface $bios, OsInterface $os)
{
$this->bios = $bios;
$this->os = $os;
}
public function turnOn()
{
$this->bios->execute();
$this->bios->waitForKeyPress();
$this->bios->launch($this->os);
}
public function turnOff()
{
$this->os->halt();
$this->bios->powerDown();
}
}OsInterface.php
namespace DesignPatterns\Structural\Facade;
interface OsInterface
{
public function halt();
}BiosInterface.php
namespace DesignPatterns\Structural\Facade;
interface BiosInterface
{
public function execute();
public function waitForKeyPress();
public function launch(OsInterface $os);
public function powerDown();
}Other Behavioral Patterns Covered
The article also briefly describes Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Null Object, Strategy, Template, Visitor, Specification, and MVC patterns, outlining their intent, typical use cases, advantages, and drawbacks.
Reference Links
https://github.com/domnikl/DesignPatternsPHP
https://laravelacademy.org/category/design-patterns
https://www.runoob.com/design-pattern/design-pattern-tutorial.html
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.