3.8. Observer

3.8.1. Amaç

Bir nesneye, yayınım/katınım (publish/subscribe) davranışını uygulanımlamak. Bir “Konu (Subject)” nesnesi durumunu değiştirdiğinde, kendisine eklenen tüm gözlemciler bilgilendirilir. Birleştirilen nesnelerin sayısını azaltmak için kullanılır ve bunu yaparken gevşek birleşimler kullanır.

3.8.2. Örnekler

  • a message queue system is observed to show the progress of a job in a GUI

3.8.3. Not

PHP, SplObserver ve SplSubject adında, bu desenin uygulanımında yardımcı olabilecek iki adet arayüze sahiptir.

3.8.4. UML Diyagramı

Alt Observer UML Diyagramı

3.8.5. Kod

Bu kodu Github üzerinde de bulabilirsiniz.

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}