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

    设计模式——观察者模式

    定义

    1. 有时被称作发布/订阅模式,观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

    2. 模式角色

      • 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
      • 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知。
      • 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
      • 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
    3. 适用场景

      • 将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。
      • 例如事件驱动编程,就是使用的观察者模式。添加事件就是其实就是添加一个观察者,事件名就是观察者名,事件处理程序就是主题改变时,观察者需要执行的操作。事件的发生就是,就是主题对象发生改变,会触发所有的已经注册的事件处理程序。
      • 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少个对象待改变。
      • 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换句话说,你不希望这些对象是紧密耦合的。
      • 发布-订阅(Publish-Subscribe)模式、模型-视图(Model-View)模式、源-监听(Source-Listener)模式、或从属者(Dependents)模式

    代码

    简单观察者

    <?php
    /**
     * 观察者模式
     * @author: Mac
     * @date: 2012/02/22
     */
     
     
    class Paper{ /* 主题    */
        private $_observers = array();
     
        public function register($sub){ /*  注册观察者 */
            $this->_observers[] = $sub;
        }
     
         
        public function trigger(){  /*  外部统一访问    */
            if(!empty($this->_observers)){
                foreach($this->_observers as $observer){
                    $observer->update();
                }
            }
        }
    }
     
    /**
     * 观察者要实现的接口
     */
    interface Observerable{
        public function update();
    }
     
    class Subscriber implements Observerable{
        public function update(){
            echo "Callback
    ";
        }
    }
    
    $paper = new Paper();
    $paper->register(new Subscriber());
    //$paper->register(new Subscriber1());
    //$paper->register(new Subscriber2());
    $paper->trigger();
    

    完整观察者

    <?php
    /**
     * 当我们在星际中开地图和几家电脑作战的时候,电脑的几个玩家相当于结盟,一旦我们出兵进攻某一家电脑,
     * 其余的电脑会出兵救援。
     * 那么如何让各家电脑知道自己的盟友被攻击了呢?并且自动做出反应?
     * 待解决的问题:一旦某个电脑被我们进攻,其他电脑就获知,并且自动出兵救援。
     *
     * 思路:为电脑设置一些额外的观察系统,由他们去通知其他电脑。
     *
     * 用途总结:观察者模式可以将某个状态的变化立即通知所有相关的对象,并调用对方的处理方法。
     *
     * 实现总结:需要一个观察者类来处理变化,被观察的对象需要实现通知所有观察者的方法。
     *
     * 将一个系统分割成一个一些类相互协作的类有一个不好的副作用,那就是需要维护相关对象间的一致性。
     * 我们不希望为了维持一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。观察者就是解决这类的耦合关系的。
     * 模式中的角色
     *
     * 抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。
     * 抽象主题提供一个接口,可以增加和删除观察者对象。
     *
     * 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;
     * 在具体主题内部状态改变时,给所有登记过的观察者发出通知。
     *
     * 抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
     *
     * 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
     *
     * 为了维护多个对象一定程度上的一致性,这些对象通常用于统一完成一个任务。
     * 那个这些对象集合可以称为一个主题。每个对象也会产生一个观察者。
     * 在创建主题的时候,添加这些观察者对象,
     * 那一个对象发生变化时,能过主题来通知其它对象
     */
    //抽象的结盟类
    abstract class abstractAlly
    {
        //放置观察者的集合,这里以简单的数组来直观演示
        public $oberserverCollection;
    
        //增加观察者的方法,参数为观察者(也是玩家)的名称
        public function addOberserver($oberserverName)
        {
            //以元素的方式将观察者对象放入观察者的集合
            $this->oberserverCollection[] = new oberserver($oberserverName);
        }
    
        //将被攻击的电脑的名字通知各个观察者
        public function notify($beAttackedPlayerName)
        {
            //把观察者的集合循环
            foreach ($this->oberserverCollection as $oberserver)
            {
                //调用各个观察者的救援函数,参数为被攻击的电脑的名字,if用来排除被攻击的电脑的观察者
                if($oberserver->name != $beAttackedPlayerName) $oberserver->help($beAttackedPlayerName);
            }
        }
    
        abstract public function beAttacked($beAttackedPlayer);
    }
    
    //具体的结盟类
    class Ally extends abstractAlly
    {
        //构造函数,将所有电脑玩家的名称的数组作为参数
        //结盟的时候,就添加每个玩家的观察者
        public function __construct($allPlayerName)
        {
            //把所有电脑玩家的数组循环
            foreach ($allPlayerName as $playerName)
            {
                //增加观察者,参数为各个电脑玩家的名称
                $this->addOberserver($playerName);
            }
        }
    
        //将被攻击的电脑的名字通知各个观察者
        public function beAttacked($beAttackedPlayerName)
        {
            //调用各个观察者的救援函数,参数为被攻击的电脑的名字,if用来排除被攻击的电脑的观察者
            $this->notify($beAttackedPlayerName);
        }
    }
    
    //观察者的接口
    interface Ioberserver
    {
        //定义规范救援方法
        function help($beAttackedPlayer);
    }
    
    //具体的观察者类
    class oberserver implements Ioberserver
    {
        //观察者(也是玩家)对象的名字
        public $name;
    
        //构造函数,参数为观察者(也是玩家)的名称
        public function __construct($name)
        {
            $this->name = $name;
        }
    
        //观察者进行救援的方法
        public function help($beAttackedPlayerName)
        {
            //这里简单的输出,谁去救谁,最后加一个换行,便于显示
            echo $this->name." help ".$beAttackedPlayerName."<br>";
        }
    }
    
    //假设我一对三,两家虫族,一家神族
    $allComputePlayer = array('Zerg1', 'Protoss2', 'Zerg2');
    
    //新建电脑结盟
    $Ally = new Ally($allComputePlayer);
    
    //假设我进攻了第二个虫族
    $Ally->beAttacked('Zerg2');
  • 相关阅读:
    算法训练 P1103
    算法训练 表达式计算
    算法训练 表达式计算
    基础练习 时间转换
    基础练习 字符串对比
    Codeforces 527D Clique Problem
    Codeforces 527C Glass Carving
    Codeforces 527B Error Correct System
    Codeforces 527A Glass Carving
    Topcoder SRM 655 DIV1 250 CountryGroupHard
  • 原文地址:https://www.cnblogs.com/xyloo/p/4142715.html
Copyright © 2011-2022 走看看