观察者模式

观察者模式:

定义对象的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

设计原则:

在观察者模式中,会改变的是主题(被观察者)的状态以及观察者的数目。用这个模式,你可以改变依赖于主题状态的对象,却不必改变主题。——找出程序中会变化的方面,然后将其和固定不变的方面相分离!

主题和观察者都使用接口:观察者利用主题的接口向主题注册,而主题利用观察者接口通知观察者。这样可以让两者之间运作正常,又同时具有松耦合的优点! ——针对接口编程,不针对实现编程!

观察者模式利用“组合”将许多观察者组合进主题中。对象(观察者——主题)之间的这种关系不是通过继承产生的,而是在运行时利用组合的方式产生的。 ——多用组合,少用继承!

使用场景:

订单创建完成后会发送EMAIL和改变订单状态等:http://blog.csdn.net/initphp/article/details/7708006

代码:

<?php

/*
 * 观察者模式
 * @date:
 */

/*
 * 主题类
 */
class Paper{
	private $_ob = array();
	
	public function register(Ob $ob){  //注册观察者
		$this->_ob[] = $ob;
	}
	
	public function trigger(){
		if(!empty($this->_ob)){
			foreach($this->_ob as $ob){
				$ob->update();
			}
		}
	}
}

/*
 * 观察者接口
 */
interface Ob{
	public function update();
}

class MyOb implements Ob{
	public function update(){
		echo get_class($this),'<br/>';
	}
}

$obj = new Paper();
$obj->register(new MyOb);
$obj->trigger();

 

观察者模式太常用了,php标准库提供了对应的接口:

观察者接口SplObserver

SplObserver {
/* 方法 */
abstract public void update ( SplSubject $subject )

}

主题(被观察者)接口SplSubject

SplSubject {
/* 方法 */
abstract public void attach ( SplObserver $observer )
abstract public void detach ( SplObserver $observer )
abstract public void notify ( void )

}

 SplSubjectStorage接口

SplObjectStorage implements Countable , Iterator , Serializable , ArrayAccess {
}

 

php手册中的例子:

<?php

/**
* Subject,that who makes news
*/
class Newspaper implements \SplSubject{
    private $name;
    //private $observers = array();
    private $observers;
    private $content;
    
    public function __construct($name) {
        $this->name = $name;
        $this->observers = new \SplObjectStorage(); 
    }

    //add observer
    public function attach(\SplObserver $observer) {
       // $this->observers[] = $observer;
        $this->observers->attach( $observer );
    }
    
    //remove observer
    public function detach(\SplObserver $observer) {
        
        /*
        $key = array_search($observer,$this->observers, true);
        if($key){
            unset($this->observers[$key]);
        }
        */
         $this->observers->detach( $observer );
    }
    
    //set breakouts news
    public function breakOutNews($content) {
        $this->content = $content;
        $this->notify();
    }
    
    public function getContent() {
        return $this->content." ({$this->name})";
    }
    
    //notify observers(or some of them)
    public function notify() {
        foreach ($this->observers as $value) { //SplObjectStorage实现了Iterator
            $value->update($this);
        }
    }
}

/**
* Observer,that who recieves news
*/
class Reader implements SplObserver{
    private $name;
    
    public function __construct($name) {
        $this->name = $name;
    }
    
    public function update(\SplSubject $subject) {
        echo $this->name.' is reading breakout news <b>'.$subject->getContent().'</b><br>';
    }
}

$newspaper = new Newspaper('Newyork Times');

$allen = new Reader('Allen');
$jim = new Reader('Jim');
$linda = new Reader('Linda');

//add reader
$newspaper->attach($allen);
$newspaper->attach($jim);
$newspaper->attach($linda);

//remove reader
$newspaper->detach($linda);

//set break outs
$newspaper->breakOutNews('USA break down!');

//=====output======
//Allen is reading breakout news USA break down! (Newyork Times)
//Jim is reading breakout news USA break down! (Newyork Times)

  

 总结:

当新对象要填入的时候,只需要在主题(又叫可观察者)中进行注册(注册方式很多,你也可以在构造的时候,或者框架访问的接口中进行注册),然后实现代码直接在新对象的接口中进行。这降低了主题对象和观察者对象的耦合度。

好的设计模式不会直接进入你的代码中,而是进入你的大脑中。

posted @ 2014-12-16 16:31  leezhxing  阅读(386)  评论(0编辑  收藏  举报