Home » Code » 常见设计模式之——观察者模式

常见设计模式之——观察者模式

下面说一个名气更大逼格更高的观察者模式。也称订阅&发布模式。它的主要内容是:一个对象(被观察者,也称发布者)的变化会引起其他对象(对观察者,也称订阅者)的变化。被观察者一般有这三个方法添加观察者、删除观察者和通知观察者,还有一个属性存储所有观察者。观察者则有一个更新的方法,在收到被观察者的变化通知时进行更新操作。

通过一个js实例或许能更直观的体会观察者模式。假设一个网页要根据访问者性别切换广告风格,广告区域名有上中下三块,我们需要方便的控制某一区域能根据性别的变化进行切换,也就是性别变化时,我说这一块要换就换,不换就不换。HTML布局如下:

<h1>观察者模式</h1>
<select id="select">
	<option value="male">男式风格</option>
	<option value="female">女式风格</option>
</select>
<button id="button">观察尾部</button>
<div id="top">
	<h2>头部广告区</h2>
	<div class="content">默认头部广告</div>
</div>
<div id="middle">
	<h2>中部广告区</h2>
	<div class="content">默认中部广告</div>
</div>
<div id="bottom">
	<h2>尾部广告区</h2>
	<div class="content">默认尾部广告</div>
</div>

三个广告区域有默认的广告,默认情况下切换风格时上中两部分会变化,尾部可以通过点击按钮控制是否跟着变化。看javascript在这个实例里面应用观察者模式:

var $ = function(id){
	return document.getElementById(id);
}
var select = $("select");//被观察者
select.observer = {};//所有观察者

select.attach = function(key, elem){
	this.observer[key] = elem;//添加观察者
}
select.detach = function(key){
	delete this.observer[key];//删除观察者
}
select.onchange = select.notify = function(){
	for(var key in this.observer){
		this.observer[key].update.call(this.observer[key], this);//通知观察者
	}
}

var topArea = $("top");
var topContent = topArea.querySelector(".content");//观察者1
topContent.update = function(subject){
	var sex = subject.value;
	if (sex === "female"){
		this.innerHTML = "整容就找XX市大北路";
		this.classList.add("male");
	}else{
		this.innerHTML = "NBA新赛季开始啦!";
		this.classList.remove("male");
	}
}
var middleArea = $("middle");
var middleContent = middleArea.querySelector(".content");//观察者2
middleContent.update = function(subject){
	var sex = subject.value;
	if (sex === "female"){
		this.innerHTML = "大长腿不是梦";
		this.classList.add("male");
	}else{
		this.innerHTML = "新款奥迪A8";
		this.classList.remove("male");
	}
}
var bottomArea = $("bottom");
var bottomContent = bottomArea.querySelector(".content");//观察者3
bottomContent.update = function(subject){
	var sex = subject.value;
	if (sex === "female"){
		this.innerHTML = "来吧灰姑娘";
		this.classList.add("male");
	}else{
		this.innerHTML = "跑男来了";
		this.classList.remove("male");
	}
}

//默认只观察头部中部
select.attach("top", topContent);
select.attach("middle", middleContent);

//添加或删除尾部为观察者
$("button").onclick = function(e){
	var dataAttach = this.getAttribute("data-attach");
	if (dataAttach === null){
		select.attach("bottom", bottomContent);
		this.setAttribute("data-attach", true);
		this.innerHTML = "不观察尾部";
	}else{
		select.detach("bottom");
		this.removeAttribute("data-attach");
		this.innerHTML = "观察尾部";
	}
}


你可以点击上边的demo体会一下。

使用观察者模式,新增一块区域的时候,要控制是否跟着变化就比较方便了。上边是在javascript中的使用,那么在PHP中如何使用观察者模式呢。其实PHP在 5.1的时候提供了实现观察者的接口:SPLObserver&SPLSubject。我们以登陆为例子,在登陆方法中,根据用户的爱好、登陆IP通知广告模块、安全模式做出推广与安全提醒之类的工作。登陆方法只做登陆相关的,其他工作都交给相关模块处理,添加其他模块为观察者观察用户,用户一登陆就发出通知,相应模块做出相应处理。

//user是被观察者
class User implements SplSubject
{
	private static $_hobby = array("篮球", "看电影");
	private static $_loginIp = array('1.1.1.1', '2.2.2.2');

	public $hobby;
	public $loginIp;
	protected $observers;//储存观察者,使用自PHP 5.3起提供SplObjectStorage类来储存

	public function __construct()
	{
		shuffle(self::$_hobby);
		shuffle(self::$_loginIp);
		$this->hobby = self::$_hobby[0];
		$this->loginIp = self::$_loginIp[0];
		$this->observers = new SplObjectStorage();
	}

	public function login()
	{
		//一系列登陆操作,session操作等...
		$this->notify();//通知观察者,我登陆了,你们要干嘛就干嘛
	}

	public function attach(SplObserver $observer)
	{
		$this->observers->attach($observer);//PHP内部已做好相关工作,不需要键名,不会重复
	}

	public function detach(SplObserver $observer)
	{
		$this->observers->detach($observer);
	}

	public function notify()
	{
		$this->observers->rewind();//指针指向第一个对象
		while($this->observers->valid())
		{
			$observer = $this->observers->current();//当前对象
			$observer->update($this);
			$this->observers->next();//下移一步指针
		}
	}

}

//广告模块是观察者
class Ad implements SplObserver
{
	public function update(SplSubject $subject)
	{
		if ($subject->hobby === '篮球')
		{
			echo "乔丹新款篮球即将售<br/>";
		}
		elseif ($subject->hobby === '看电影')
		{
			echo "速度与激情7火热上映</br>";
		}
		else
		{
			echo '1024,好人一生平安</br>';
		}
	}
}

//安全模块是观察者
class security implements SplObserver
{
	public function update(SplSubject $subject)
	{
		if ($subject->loginIp === '1.1.1.1')
		{
			echo "登陆正常</br>";
		}
		elseif ($subject->hobby === '2.2.2.2')
		{
			echo "登陆异常</br>";
		}
		else
		{
			echo '飘到外太空啦</br>';
		}
	}
}

//用户来登陆
$user = new User();
$this->attach(new security());//添加观察者
$this->attach(new Ad());
$user->login();

//输出
// 飘到外太空啦
// 乔丹新款篮球即将售

Leave a Reply

Your email address will not be published. Required fields are marked *

*

Time limit is exhausted. Please reload CAPTCHA.