Mastering PHP Design Patterns: From Singleton to Command Explained
This article provides a comprehensive guide to PHP design patterns, covering fundamentals such as cloning, singleton, factory, abstract factory, composite, decorator, facade, strategy, observer, visitor, and command patterns with clear explanations and practical code examples.
What are Design Patterns?
After years of software development, Gamma and others discovered that certain recurring designs, like architectural templates for rooms, can be applied to software as design patterns, enabling faster and more robust development.
1. Syntax – Clone
To operate on an original object without affecting it, PHP’s clone creates a shallow copy; primitive types and arrays are deep‑copied, while object properties are shallow. Implement __clone() to customize cloning behavior. $K_back = clone $K; Example of __clone:
function __clone(){
$this->object = clone $this->object;
}2. Patterns
2.1 Composite
Problem: Classroom class is inherited by Lecture and Seminar, both need one‑time and per‑session billing. Solution 1 adds billing methods to the base class; solution 2 extracts billing into a separate strategy class.
2.2 Singleton
Ensures only one instance exists, e.g., for configuration.
class Preferences {
private static $instance;
private function __construct(){ /* ... */ }
public static function get_instance(){
if (empty(self::$instance)){
self::$instance = new Preferences();
}
return self::$instance;
}
}2.3 Factory
Creates related objects through a common interface, e.g., different content providers for Evernote.
abstract class Show_Evernote {
abstract function get_header_text();
abstract function get_context();
// …
}
class Show_SinaWeibo extends Show_Evernote {
function get_header_text(){ /* ... */ }
function get_context(){ /* new SinaWeiboContent */ }
// …
}2.4 Abstract Factory
Provides families of related objects (content, account, comments) for different platforms, allowing interchangeable factories for Sina Weibo, Developer Headlines, etc.
2.5 Parallel (Prototype) Pattern
Similar to abstract factory but uses concrete classes for easier instantiation, enabling flexible composition of components.
3. Object Creation
Examples demonstrate creating objects using the patterns above, such as obtaining a singleton instance, building a product via a factory, or assembling a composite structure.
4. Using Objects
4.1 Composite Pattern
Manages a hierarchy where a "woman" object aggregates multiple "man" services (massage, foot‑massage, etc.) and iterates over them to calculate overall comfort.
$woman = new Person();
$woman->add_man(new FootMan());
$woman->add_man(new BackMan());
$woman->calculate_comfort();4.2 Decorator Pattern
Wraps a base object to add responsibilities without altering its interface; for example, a basic "Man" can be decorated with massage, shampoo, or foot‑massage capabilities.
class Man extends Person { private $well = 10; function get_well(){ return $this->well; } }
abstract class ManDecorator extends Person { protected $person; function __construct(Person $person){ $this->person = $person; } }
class BackMassageDecorator extends ManDecorator { function get_well(){ return $this->person->get_well() + 30; } }
// Usage
$person = new BackMassageDecorator(new Man());
$person->get_well(); // 404.3 Facade Pattern
Provides a simple interface to a complex subsystem, such as a controller exposing clear methods to the view while the model contains tangled logic.
5. Executing Tasks
5.1 Strategy Pattern
Encapsulates payment methods; the client sets a strategy (Alipay, WeChat, cash) and invokes pay() without the payment logic residing in the main class.
abstract class Payment { abstract function pay($amount); }
class AlipayPayment extends Payment { function pay($amount){ return external_alipay_process($amount); } }
class Person {
protected $paymentMethod;
function setPaymentMethod(Payment $method){ $this->paymentMethod = $method; }
function pay($amount){ return $this->paymentMethod->pay($amount); }
}5.2 Observer Pattern
Subjects notify observers of state changes; example includes a Login subject triggering EmailObserver and LogObserver when a user logs in.
interface Subject { function attach(Observer $o); function detach(Observer $o); function notify(); }
class Login implements Subject {
private $observers = [];
function attach(Observer $o){ $this->observers[] = $o; }
function detach(Observer $o){ /* remove */ }
function notify(){ foreach($this->observers as $o){ $o->update($this); } }
}
interface Observer { function update(Subject $s); }
class EmailObserver implements Observer { function update(Subject $s){ /* send email */ } }
class LogObserver implements Observer { function update(Subject $s){ /* write log */ } }5.3 Visitor Pattern
Separates algorithms from object structures; a visitor traverses an army hierarchy to compute combat strength, allowing new operations without modifying the element classes.
abstract class ArmyVisitor { abstract function visitUnit(Unit $u); }
class CombatStrengthVisitor extends ArmyVisitor {
private $text = "";
function visitUnit(Unit $u){
$pad = str_repeat(" ", $u->getDepth()*4);
$this->text .= $pad . get_class($u) . ": combat=" . $u->combatStrength() . "
";
}
function getText(){ return $this->text; }
}
abstract class Unit { abstract function accept(ArmyVisitor $v); protected $depth; function setDepth($d){ $this->depth=$d; } function getDepth(){ return $this->depth; } }
class Soldier extends Unit { function combatStrength(){ return 48; } function accept(ArmyVisitor $v){ $v->visitUnit($this); } }
// Usage
$army = new Army();
$army->addUnit(new Soldier());
$visitor = new CombatStrengthVisitor();
$army->accept($visitor);
echo $visitor->getText();5.4 Command Pattern
Encapsulates actions such as login, feedback, logout as command objects executed by a controller, simplifying request handling and allowing easy extension.
abstract class Command { abstract function execute(CommandContext $ctx); }
class LoginCommand extends Command {
function execute(CommandContext $ctx){
$manager = Register::getAccessManager();
$user = $ctx->get('username');
$pass = $ctx->get('pass');
$obj = $manager->login($user,$pass);
if (is_null($obj)){
$ctx->setError($manager->getError());
return false;
}
$ctx->addParam('user',$obj);
return true;
}
}
class CommandFactory { public function getCommand($action){ $class = ucfirst(strtolower($action)).'Command'; return new $class(); } }
class Controller {
private $context;
function __construct(){ $this->context = new CommandContext(); }
function process(){
$factory = new CommandFactory();
$cmd = $factory->getCommand($this->context->get('action'));
if(!$cmd->execute($this->context)){
// handle error
} else {
// render view
}
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
21CTO
21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.
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.
