php觀察折模式

<?php
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 $name = ""; 
  public function __construct($name){
    $this->name = $name;
  }
  public function update(){
    echo $this->name." callback\n";
  }
}


$paper = new Paper();
$paper->register(new Subscriber('xiaoming'));
$paper->register(new Subscriber('xiaoli'));
$paper->trigger();

  被觀察者經過自身的接口,將觀察者註冊到本身的屬性裏。php

當觸發調用的時候經過觀察者統一實現的接口方法通知觀察者。html

 

當有新的觀察者進來的時候,只須要新觀察者實現接口,而後註冊到被觀察者屬性裏就能夠啦。apache

這下降了主題對象和觀察者對象的耦合度。編程

設計原則this

在觀察者模式中,會改變的是主題的狀態以及觀察者的數目。用這個模式,你能夠改變依賴於主題狀態的對象,卻沒必要改變主題。——找出程序中會變化的方面,而後將其和固定不變的方面相分離!
 
 主題和觀察者都使用接口:觀察者利用主題的接口向主題註冊,而主題利用觀察者接口通知觀察者。這樣能夠讓二者之間運做正常,又同時具備鬆耦合的優勢! ——針對接口編程,不針對實現編程!
 觀察者模式利用「組合」將許多觀察者組合進主題中。對象(觀察者——主題)之間的這種關係不是經過繼承產生的,而是在運行時利用組合的方式產生的。 ——多用組合,少用繼承!

 http://www.cnblogs.com/baochuan/archive/2012/02/22/2362668.htmlspa

 

 

Thinkphp3.2.3就是經過這個模式來實現的。插件

\Tinkphp\Core\Library\Think\Think.class.php設計

81-83行,經過這個註冊,觀察者;日誌

          // 加載應用行爲定義
          if(is_file(CONF_PATH.'tags.php'))
              // 容許應用增長開發模式配置定義
              Hook::import(include CONF_PATH.'tags.php');   

  附上被觀察者源碼;server

\Thinkphp\Core\Library\Think\Hook.class.php

<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2013 http://topthink.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think;
/**
 * ThinkPHP系統鉤子實現
 */
class Hook {

    static private  $tags       =   array();

    /**
     * 動態添加插件到某個標籤
     * @param string $tag 標籤名稱
     * @param mixed $name 插件名稱
     * @return void
     */
    static public function add($tag,$name) {
        if(!isset(self::$tags[$tag])){
            self::$tags[$tag]   =   array();
        }
        if(is_array($name)){
            self::$tags[$tag]   =   array_merge(self::$tags[$tag],$name);
        }else{
            self::$tags[$tag][] =   $name;
        }
    }

    /**
     * 批量導入插件
     * @param array $data 插件信息
     * @param boolean $recursive 是否遞歸合併
     * @return void
     */
    static public function import($data,$recursive=true) {
        if(!$recursive){ // 覆蓋導入
            self::$tags   =   array_merge(self::$tags,$data);
        }else{ // 合併導入
            foreach ($data as $tag=>$val){
                if(!isset(self::$tags[$tag]))
                    self::$tags[$tag]   =   array();            
                if(!empty($val['_overlay'])){
                    // 能夠針對某個標籤指定覆蓋模式
                    unset($val['_overlay']);
                    self::$tags[$tag]   =   $val;
                }else{
                    // 合併模式
                    self::$tags[$tag]   =   array_merge(self::$tags[$tag],$val);
                }
            }            
        }
    }

    /**
     * 獲取插件信息
     * @param string $tag 插件位置 留空獲取所有
     * @return array
     */
    static public function get($tag='') {
        if(empty($tag)){
            // 獲取所有的插件信息
            return self::$tags;
        }else{
            return self::$tags[$tag];
        }
    }

    /**
     * 監聽標籤的插件
     * @param string $tag 標籤名稱
     * @param mixed $params 傳入參數
     * @return void
     */
    static public function listen($tag, &$params=NULL) {
        if(isset(self::$tags[$tag])) {
            if(APP_DEBUG) {
                G($tag.'Start');
                trace('[ '.$tag.' ] --START--','','INFO');
            }
            foreach (self::$tags[$tag] as $name) {
                APP_DEBUG && G($name.'_start');
                $result =   self::exec($name, $tag,$params);
                if(APP_DEBUG){
                    G($name.'_end');
                    trace('Run '.$name.' [ RunTime:'.G($name.'_start',$name.'_end',6).'s ]','','INFO');
                }
                if(false === $result) {
                    // 若是返回false 則中斷插件執行
                    return ;
                }
            }
            if(APP_DEBUG) { // 記錄行爲的執行日誌
                trace('[ '.$tag.' ] --END-- [ RunTime:'.G($tag.'Start',$tag.'End',6).'s ]','','INFO');
            }
        }
        return;
    }

    /**
     * 執行某個插件
     * @param string $name 插件名稱
     * @param string $tag 方法名(標籤名)     
     * @param Mixed $params 傳入的參數
     * @return void
     */
    static public function exec($name, $tag,&$params=NULL) {
        if('Behavior' == substr($name,-8) ){
            // 行爲擴展必須用run入口方法
            $tag    =   'run';
        }
        $addon   = new $name();
        return $addon->$tag($params);
    }
}
相關文章
相關標籤/搜索