3.8. Observer
3.8.1. Scopo
Implementare un meccanismo di publish/subscribe su di un oggetto. Ogni volta che l’oggetto «Subjet» cambia di stato, gli «Observers» attaccati ad esso saranno notificati riducendo l’ammontare di oggetti accoppiati.
3.8.2. Esempi
Una coda di messaggi è osservata per mostrare l’avanzamento di un task in una GUI
3.8.3. Note
PHP definisce già due interfacce per implementare questo pattern: SplObserver e SplSubject.
3.8.4. Diagramma UML
3.8.5. Codice
Potete trovare questo codice anche su 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}