Development Blog
Collection of development articles I've put together...
Observer Pattern
Usage of this pattern allows class functionality to be dynamically extended in runtime, helping to prevent code rot and ensuring functionality is decoupled.
1
Likes
Stuart Todd
•
2 years ago
•
Design Patterns: Behavioural
Purpose
The purpose of the design pattern
A software pattern in which an object (subject) maintains a list of dependents (observers) and notifies them of state changes, usually by calling one of their methods. Usage of this pattern allows class functionality to be dynamically extended in runtime, helping to prevent code rot and ensuring functionality is decoupled.
Note: PHP already defines two interfaces that can help to implement this pattern: SplObserver and SplSubject.
UML
UML design pattern diagram
Code
Code snippets
User
User implements SplSubject. It maintains a list of observers and sends notifications to them if changes are made to the User object (by calling notify).
SplObjectStorage is an object which stores the list of observers, this list can be modified using the attach and detach methods. Notify loops through the observers and calls a method (enforced by an interface) for each observer.
namespace DesignPatterns\Behavioral\Observer;
use SplSubject;
use SplObjectStorage;
use SplObserver;
/**
* User implements the observed object (called Subject), it maintains a list of observers and sends notifications to
* them in case changes are made on the User object
*/
class User implements SplSubject
{
private SplObjectStorage $observers;
private $email;
public function __construct()
{
$this->observers = new SplObjectStorage();
}
public function attach(SplObserver $observer): void
{
$this->observers->attach($observer);
}
public function detach(SplObserver $observer): void
{
$this->observers->detach($observer);
}
public function changeEmail(string $email): void
{
$this->email = $email;
$this->notify();
}
public function notify(): void
{
/** @var SplObserver $observer */
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
}
UserObserver
UserObserver (observer) will be attached to User (the subject). If the User object is changed, then we’ll notify all the attached observers about the change.
The Observer implements SplObserver. This code example builds an array of changedUsers, however an Observer is generally useful for various scenarios which help us extend code, e.g if the ‘subject’ was an HTML page object, then the observers could be HTML components objects which build the page (different components could be attached based on which page you’re visiting and/or business logic).
Once the HTML page is ready to be rendered you’d notify each attached component and get the HTML component data from each.
The Observer implements SplObserver. This code example builds an array of changedUsers, however an Observer is generally useful for various scenarios which help us extend code, e.g if the ‘subject’ was an HTML page object, then the observers could be HTML components objects which build the page (different components could be attached based on which page you’re visiting and/or business logic).
Once the HTML page is ready to be rendered you’d notify each attached component and get the HTML component data from each.
namespace DesignPatterns\Behavioral\Observer;
use SplObserver;
use SplSubject;
class UserObserver implements SplObserver
{
/**
* @var SplSubject[]
*/
private array $changedUsers = [];
/**
* It is called by the Subject, usually by SplSubject::notify()
*/
public function update(SplSubject $subject): void
{
$this->changedUsers[] = clone $subject;
}
/**
* @return SplSubject[]
*/
public function getChangedUsers(): array
{
return $this->changedUsers;
}
}