編程中的那些套路——關於觀察者模式

該文章屬於《編程中的那些經典套路——設計模式彙總》系列,而且如下內容基於語言PHPphp

今天咱們來談談觀察者模式,這是一個常常用到的設計模式。sql

讓咱們想象一個場景:一個網站有不一樣等級區域的用戶,不一樣等級的的用戶登陸後能夠得到對應的服務(一級用戶登陸後得到一級服務,二級用戶登陸後得到二級服務,…以此類推)。那麼咱們如何寫這段業務邏輯呢?編程

按照通常思路:咱們會寫N個if..else判斷,像下面這樣:segmentfault

//登陸操做..省略
 
if(一級用戶) {
 
echo ' 一級服務';
 
} else if(二級用戶){
 
echo '二級服務';
 
} else if(三級用戶){
 
echo '三級服務';
 
}

但這樣寫代碼有一個弊端,若是咱們又增長了一個等級用戶,那麼咱們是否是要修改原來的代碼呢(增長多一個if..else判斷),這樣作是很是不穩當的,由於寫好的代碼咱們不該該碰它。設計模式

咱們應該寫一段拓展性強與維護性較強的代碼,由此衍生出觀察者模式。數組

觀察者模式的大體思路是這樣的:有一個觀察者列表(A),有一個被觀察者列表(B),當B發生變化時,程序就會遍歷觀察者列表A,隨之執行對應的update操做,而後得到想要的效果。語言表述可能比較難以理解,咱們來看代碼吧。網站

PHP已經幫咱們內置了一個觀察者模式的接口(The SplSubject interface),咱們能夠直接實現這個接口:this

圖片描述
而且php還提供了一個存儲對象的class(即觀察者列表):spa

圖片描述

固然咱們徹底能夠不使用這個類,能夠用數組代替。設計

具體代碼:

觀察者模式.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,從而達到不一樣的用戶得到不一樣的服務目的,而當咱們須要新增/減小用戶等級的時候又不須要修改源代碼,很好的符合了開放封閉原則。

我一直認爲觀察者模式單例模式工廠模式三者都是很棒的設計模式,但觀察者模式理解起來稍微比較困難,若是有困惑的話能夠直接在評論區發問。

該文章屬於《編程中的那些經典套路——設計模式彙總》系列

相關文章
相關標籤/搜索