PHP 设计模式 - 行为型 - 观察者模式(Observer)

1. 模式定义

观察者模式有时也被称作发布/订阅模式,该模式用于为对象实现发布/订阅功能:一旦主体对象状态发生改变,与之关联的观察者对象会收到通知,并进行相应操作。
将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。 消息队列系统、事件都使用了观察者模式。

PHP 为观察者模式定义了两个接口:SplSubjectSplObserverSplSubject 可以看做主体对象的抽象,SplObserver 可以看做观察者对象的抽象,要实现观察者模式,只需让主体对象实现 SplSubject ,观察者对象实现 SplObserver,并实现相应方法即可。

2. UML类图

image.png

3. 示例代码

User.php 实现SplSubject的对象

<?php

namespace DesignPattern\Behavioral\Observer;

use SplObserver;

/**
 * 观察者模式 : 被观察对象 (主体对象)
 *
 * 主体对象维护观察者列表并发送通知
 *
 */
class User implements \SplSubject
{

    /**
     * 用户数据
     *
     * @var array
     */
    protected $data = array();

    /**
     * 观察者对象集
     *
     * @var \SplObjectStorage
     */
    protected $observers;

    public function __construct()
    {
        $this->observers = new \SplObjectStorage();
    }

    /**
     * 附加观察者
     * @link https://php.net/manual/en/splsubject.attach.php
     * @param SplObserver $observer <p>
     * The <b>SplObserver</b> to attach.
     * </p>
     * @return void
     * @since 5.1.0
     */
    public function attach(SplObserver $observer)
    {
        $this->observers->attach($observer);
    }

    /**
     * 取消观察者
     * @link https://php.net/manual/en/splsubject.detach.php
     * @param SplObserver $observer <p>
     * The <b>SplObserver</b> to detach.
     * </p>
     * @return void
     * @since 5.1.0
     */
    public function detach(SplObserver $observer)
    {
        $this->observers->detach($observer);
    }

    /**
     * 通知观察者此用户进行更新
     * @link https://php.net/manual/en/splsubject.notify.php
     * @return void
     * @since 5.1.0
     */
    public function notify()
    {
        /** @var \SplObserver $observer */
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    /**
     *
     * 设置/更新用户数据
     * @param string $name
     * @param mixed  $value
     *
     * @return void
     */
    public function __set($name, $value)
    {
        $this->data[$name] = $value;

        // 通知观察者用户被改变
        $this->notify();
    }
}

UserObserver.php 实现SplObserver的对象

<?php

namespace DesignPattern\Behavioral\Observer;

use SplSubject;

class UserObserver implements \SplObserver
{

    /**
     * 收到观察对象被更新的消息,进行操作
     * @link https://php.net/manual/en/splobserver.update.php
     * @param SplSubject $subject <p>
     * 这个 <b>SplSubject</b>通知观察者更新
     * </p>
     * @return void
     * @since 5.1.0
     */
    public function update(SplSubject $subject)
    {
        echo get_class($subject) . ' 被更新';
    }
}

单元测试

<?php

namespace DesignPattern\Tests;

use DesignPattern\Behavioral\Observer\User;
use DesignPattern\Behavioral\Observer\UserObserver;
use PHPUnit\Framework\TestCase;

/**
 * 观察者模式测试
 * Class ObserverTest
 * @package DesignPattern\Tests
 */
class ObserverTest extends TestCase
{

    protected $observer;

    protected function setUp(): void
    {
        $this->observer = new UserObserver();
    }

    /**
     * 测试通知
     */
    public function testNotify()
    {
        $this->expectOutputString('DesignPattern\Behavioral\Observer\User 被更新');
        $subject = new User();

        $subject->attach($this->observer);
        $subject->property = 123;
    }

    /**
     * 测试订阅
     */
    public function testAttachDetach()
    {
        $subject = new User();
        $reflection = new \ReflectionProperty($subject, 'observers');

        $reflection->setAccessible(true);
        /** @var \SplObjectStorage $observers */
        $observers = $reflection->getValue($subject);

        $this->assertInstanceOf('SplObjectStorage', $observers);
        $this->assertFalse($observers->contains($this->observer));

        $subject->attach($this->observer);
        $this->assertTrue($observers->contains($this->observer));

        $subject->detach($this->observer);
        $this->assertFalse($observers->contains($this->observer));
    }

}

参考文档:https://laravelacademy.org/post/2935
教程源码:https://github.com/SylviaYuan1995/DesignPatternDemo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容