zoukankan      html  css  js  c++  java
  • 观察者模式

    模式定义

    观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

    观察者模式的核心在于Subject和Observer接口,Subject(主题目标)包含一个给定的状态,观察者“订阅”这个主题,将主题的当前状态通知观察者,每次给定状态改变时所有观察者都会得到通知。

    发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。

    在PHP的标准库(SPL)里甚至提供了三个接口SplSubjectSplObserverSplObjectStorage来让开发者更容易地实现观察者模式。

    模式结构说明

    观察者模式UML

    • Subject 目标抽象类
    • ConcreteSubject 具体目标
    • Observer 观察者抽象类
    • ConcreteObserver 具体观察者

    何时使用观察者模式

    • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
    • 一个对象必须通知其他对象,而并不知道这些对象是谁。
    • 基于事件触发机制来解耦复杂逻辑时,从整个逻辑的不同关键点抽象出不同的事件,主流程只需要关心最核心的逻辑并能正确地触发事件(Subject),其余相关功能实现由观察者或者叫订阅者来完成。

    应用举例

    <?php
    
    /**
     * 观察者模式(发布-订阅模式)
     * 定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新
     * Subject 包含一个给定的状态,观察者订阅这个主题,将主题的当前状态通知观察者,每次给定状态改变时所有观察者都会得到通知
     * 观察目标:发生改变的对象
     * 观察者:被通知的对象
     * 一个观察目标可以对应多个观察者,而观察者之间没有相互关系,可以按需增加和删除观察者
     */
    
    // 被观察父类
    class Subject implements SplSubject
    {
        private $observers = [];
        private $params = [];
    
        public function setParams($key, $value)
        {
            $this->params[$key] = $value;
    
            return $this;
        }
    
        public function getParams($key)
        {
            return $this->params[$key] ?? '';
        }
    
        /**
         * Attach an SplObserver
         * @param SplObserver $observer
         */
        public function attach(SplObserver $observer)
        {
            // TODO: Implement attach() method.
            $this->observers[] = $observer;
    
            return $this;
        }
    
        /**
         * Detach an observer
         * @param SplObserver $observer
         */
        public function detach(SplObserver $observer)
        {
            // TODO: Implement detach() method.
            $key = array_search($observer, $this->observers);
            if ($key !== false) {
                unset($this->observers[$key]);
            }
    
            return $this;
        }
    
        /**
         * Notify an observer
         */
        public function notify()
        {
            // TODO: Implement notify() method.
            foreach ($this->observers as $observer) {
                $observer->update($this);
            }
        }
    }
    
    // 具体的被观察者
    class UserSubject extends Subject
    {
        public function changePassword()
        {
            $this->notify();
        }
    }
    
    // 邮件观察者
    class EmailObserver implements SplObserver
    {
        const KEY = 'email';
    
        /**
         * Receive update from subject
         * @param SplSubject $subject
         */
        public function update(SplSubject $subject)
        {
            // TODO: Implement update() method.
            $params = $subject->getParams(self::KEY);
            $this->send($params);
        }
    
        // 获取参数发送邮件
        public function send(array $params)
        {
            echo '邮件观察者收到参数: ' . var_export($params, true) . ' 发送邮件' . PHP_EOL;
        }
    }
    
    // 短信观察者
    class SmsObserver implements SplObserver
    {
        const KEY = 'sms';
    
        /**
         * Receive update from subject
         * @param SplSubject $subject
         */
        public function update(SplSubject $subject)
        {
            // TODO: Implement update() method.
            $params = $subject->getParams(self::KEY);
            $this->send($params);
        }
    
        // 获取参数发送邮件
        public function send(array $params)
        {
            echo '短信观察者收到参数: ' . var_export($params, true) . ' 发送短信' . PHP_EOL;
        }
    }
    
    // run
    $emailObserver = new EmailObserver();
    $smsObserver = new SmsObserver();
    $userSubject = new UserSubject();
    $userSubject
        ->attach($emailObserver)
        ->attach($smsObserver)
        ->setParams(EmailObserver::KEY, ['from' => 'admin@qq.com', 'to' => 'xxx@qq.com', 'content' => '有密码修改'])
        ->setParams(SmsObserver::KEY, ['mobile' => 'xxx', 'content' => '有密码修改'])
        ->changePassword();

    参考:https://github.com/kevinyan815/Learning_Laravel_Kernel/blob/master/articles/Observer.md

  • 相关阅读:
    servlet学习之servletAPI编程常用的接口和类
    问题解决
    HTTP Status 500 – Internal Server Error
    用数组模拟队列
    稀疏数组
    值传递机制及几道网红题目
    关于Tomcat配置问题
    Servlet学习笔记
    面向对象笔记
    数组中涉及的常见算法
  • 原文地址:https://www.cnblogs.com/cshaptx4869/p/11629715.html
Copyright © 2011-2022 走看看