该文章属于《编程中的那些经典套路——设计模式汇总》系列,并且以下内容基于语言PHP
今天我们来谈谈观察者模式,这是一个经常用到的设计模式。
让我们想象一个场景:一个网站有不同等级区域的用户,不同等级的的用户登录后可以获得对应的服务(一级用户登录后获得一级服务,二级用户登录后获得二级服务,…以此类推)。那么我们如何写这段业务逻辑呢?
按照一般思路:我们会写N个if..else判断,像下面这样:
//登录操作..省略
if(一级用户) {
echo ' 一级服务';
} else if(二级用户){
echo '二级服务';
} else if(三级用户){
echo '三级服务';
}
但这样写代码有一个弊端,如果我们又增加了一个等级用户,那么我们是不是要修改原来的代码呢(增加多一个if..else判断),这样做是非常不妥当的,因为写好的代码我们不应该碰它。
我们应该写一段拓展性强与维护性较强的代码,由此衍生出观察者模式。
观察者模式的大致思路是这样的:有一个观察者列表(A),有一个被观察者列表(B),当B发生变化时,程序就会遍历观察者列表A,随之执行对应的update操作,然后获得想要的效果。语言表述可能比较难以理解,我们来看代码吧。
PHP已经帮我们内置了一个观察者模式的接口(The SplSubject interface),我们可以直接实现这个接口:
并且php还提供了一个存储对象的class(即观察者列表):
当然我们完全可以不使用这个类,可以用数组代替。
具体代码:
观察者模式.php
<?php
//LoginSubject
class LoginSubject implements SplSubject{
//观察者列表
public $observers,$value,$hobby,$address;
//初始化变量
public function __construct(){
//sqlObjectStorage是一个类,专门用来存储内容,观察者列表就是存在此类
$this->observers = new SplObjectStorage();
}
//登录
public function login(){
//登录过程,省略
$this->notify();
}
//添加观察者
public function attach(SplObserver $observer){
$this->observers->attach($observer);
}
//剔除观察者
public function detach(SplObserver $observer){
$this->observers->detach($observer);
}
//登陆后通知notify
public function notify(){
$observers = $this->observers;
//这段rewind不可或缺... 将节点指针指向第一位节点
$observers->rewind();
//当前节点存在
while($observers->valid()){
$observer = $observers->current();//获取当前节点(即观察者)
$observer->update($this);//进行update犯法操作
$observers->next();//next 节点
}
}
}
//observer User1Observers
class User1Observers implements SplObserver {
public function update(SplSubject $subject){
echo '我是一级用户,请给我对应的一级服务';
}
}
//observer User2Observers
class User2Observers implements SplObserver {
public function update(SplSubject $subject){
echo '我是二级用户,请给我对应的二级服务';
}
}
//observer CommenUserObservers
class CommenUserObservers implements SplObserver {
public function update(SplSubject $subject){
echo '我是普通用户,请给我对应的普通服务';
}
}
//如果需要的话可以继续增加或者减少用户等级,丝毫不会影响原本的等级用户
$subject = new LoginSubject();
$CommenUserObservers = new CommenUserObservers;//普通用户
$subject->attach(new User1Observers);//增加观察者:一级用户
$subject->attach(new User2Observers);//增加观察者:二级用户
$subject->attach($CommenUserObservers);//增加观察者:普通用户
$subject->login();//登录,触发notify
//output:我是一级用户,请给我对应的一级服务我是二级用户,请给我对应的二级服务我是普通用户,请给我对应的普通服务
echo '<br/>';
//如果有一天普通用户压根没有对应的服务了,那么我们就可以剔除它了
//$subject->detach(new CommenUserObservers); 无效
$subject->detach($CommenUserObservers);//删除观察者:普通用户
$subject->login();//登录,触发notify,普通用户就不会被通知啦
//output:我是一级用户,请给我对应的一级服务我是二级用户,请给我对应的二级服务
?>
看出门道了吗?每当登录操作的时候,就会顺带触发notify方法,从而遍历关注者列表内的对象方法update,从而达到不同的用户获得不同的服务目的,而当我们需要新增/减少用户等级的时候又不需要修改源代码,很好的符合了开放封闭原则。
我一直认为观察者模式、单例模式、工厂模式三者都是很棒的设计模式,但观察者模式理解起来稍微比较困难,如果有困惑的话可以直接在评论区发问。