PHP 观察者模式
目的
要实现对象的发布/订阅行为,只要[Subject]对象改变它的状态,就会通知附加的[observer]。它用于减轻对象的耦合度,并使用松耦合代替。
例子
- 通过观察消息队列系统,可以在GUI中显示作业的进度
注意
PHP已经定义了两个接口来帮助实现这个模式:SplObserver和SplSubject。
UML 图
代码
User.php
<?php declare(strict_types=1); 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.php
<?php declare(strict_types=1); 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; } }
测试
Tests/ObserverTest.php
<?php declare(strict_types=1); namespace DesignPatterns\Behavioral\Observer\Tests; use DesignPatterns\Behavioral\Observer\User; use DesignPatterns\Behavioral\Observer\UserObserver; use PHPUnit\Framework\TestCase; class ObserverTest extends TestCase { public function testChangeInUserLeadsToUserObserverBeingNotified() { $observer = new UserObserver(); $user = new User(); $user->attach($observer); $user->changeEmail('foo@bar.com'); $this->assertCount(1, $observer->getChangedUsers()); } }