3.8. Observer
3.8.1. Propósito
Para implementar un comportamiento de publicación / suscripción a un objeto, siempre que un objeto «Sujeto» cambia su estado, los «Observadores» adjuntos serán avisados. Se utiliza para acortar la cantidad de objetos acoplados y utiliza un loose coupling en su lugar.
3.8.2. Ejemplos
Se observa un sistema de cola de mensajes para mostrar el progreso de un trabajo en una GUI
3.8.3. Nota
PHP ya define dos interfaces que pueden ayudar a implementar este patrón: SplObserver y SplSubject.
3.8.4. Diagrama UML
3.8.5. Código
Puedes encontrar el código en GitHub
User.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Observer;
6
7use SplSubject;
8use SplObjectStorage;
9use SplObserver;
10
11/**
12 * User implements the observed object (called Subject), it maintains a list of observers and sends notifications to
13 * them in case changes are made on the User object
14 */
15class User implements SplSubject
16{
17 private SplObjectStorage $observers;
18 private $email;
19
20 public function __construct()
21 {
22 $this->observers = new SplObjectStorage();
23 }
24
25 public function attach(SplObserver $observer): void
26 {
27 $this->observers->attach($observer);
28 }
29
30 public function detach(SplObserver $observer): void
31 {
32 $this->observers->detach($observer);
33 }
34
35 public function changeEmail(string $email): void
36 {
37 $this->email = $email;
38 $this->notify();
39 }
40
41 public function notify(): void
42 {
43 /** @var SplObserver $observer */
44 foreach ($this->observers as $observer) {
45 $observer->update($this);
46 }
47 }
48}
UserObserver.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Observer;
6
7use SplObserver;
8use SplSubject;
9
10class UserObserver implements SplObserver
11{
12 /**
13 * @var SplSubject[]
14 */
15 private array $changedUsers = [];
16
17 /**
18 * It is called by the Subject, usually by SplSubject::notify()
19 */
20 public function update(SplSubject $subject): void
21 {
22 $this->changedUsers[] = clone $subject;
23 }
24
25 /**
26 * @return SplSubject[]
27 */
28 public function getChangedUsers(): array
29 {
30 return $this->changedUsers;
31 }
32}
3.8.6. Test
Tests/ObserverTest.php
1<?php
2
3declare(strict_types=1);
4
5namespace DesignPatterns\Behavioral\Observer\Tests;
6
7use DesignPatterns\Behavioral\Observer\User;
8use DesignPatterns\Behavioral\Observer\UserObserver;
9use PHPUnit\Framework\TestCase;
10
11class ObserverTest extends TestCase
12{
13 public function testChangeInUserLeadsToUserObserverBeingNotified()
14 {
15 $observer = new UserObserver();
16
17 $user = new User();
18 $user->attach($observer);
19
20 $user->changeEmail('foo@bar.com');
21 $this->assertCount(1, $observer->getChangedUsers());
22 }
23}