【PHP设计模式】行为型之观察者(Observer)

观察者模式:观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。

  允许一个类观察另一个类的状态,当被观察者的类发生改变时,观察类可以收到通知并作出相应的动作。

 

<UML>

 

<示例代码>

class Login {
    const LOGIN_USER_UNKNOWN = 1;
    const LOGIN_WRONG_PASS = 2;
    const LOGIN_ACCESS = 3;
    private $status = array();

    function handleLogin( $user, $pass, $ip ) {
        switch ( rand(1,3) ) {
            case 1:
                $this->setStatus( self::LOGIN_ACCESS, $user, $ip );//正常登陆
                $ret = true;
                break;
            case 2:
                $this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );//错误密码
                $ret = false;
                break;
            case 3:
                $this->setStatus( self::LOGIN_USER_UNKNOWN, $user, $ip );//权限不够
                $ret = false;
                 break;
        }
        Logger::logIP( $user, $ip, $this->getStatus() );//记录日志
        if ( ! $ret ) {
            Notifier::mailWarning( $user, $ip,$this->getStatus()  );//邮件通知
        }
        return $ret;
    }
    private function setStatus( $status, $user, $ip ) {
        $this->status = array( $status, $user, $ip );
    }
    function getStatus() {
        return $this->status;
    }
}

class Notifier {//邮件通知
    static function mailWarning( $user, $ip, array $status ) {
        print "Notifier: $user, ip: $ip status:";
        print implode("/", $status);
        print "\n";
    }

}
class Logger {//记录日志
    static function logIP( $user, $ip, array $status ) {
        print "Loggger: $user, ip: $ip status:";
        print implode("/", $status);
        print "\n";
    }
}

$login = new Login();
for ( $x=1; $x<2; $x++ ) {
    $login->handleLogin( "bob","mypass", '158.152.55.35' );
}

这里的login类实现了太多的功能,和系统的代码仅仅耦合起来了,如果它调用的对象发生改变。

<示例代码二>

/**
 * 被观察者
 */
interface Observable {
    function attach( Observer $observer );
    function detach( Observer $observer );
    function notify();
}
/**
 * 具体的被观察者类
 */
class Login implements Observable {
    private $observers = array();
    const LOGIN_USER_UNKNOWN = 1;
    const LOGIN_WRONG_PASS   = 2;
    const LOGIN_ACCESS       = 3;

    function attach( Observer $observer ) {//添加观察者
        $this->observers[] = $observer;
    }

    function detach( Observer $observer ) {//删除观察者
        $newobservers = array();
        foreach ( $this->observers as $obs ) {
            if (  ($obs !== $observer) ) {
                $newobservers[]=$obs;
            }
        }
        $this->observers = $newobservers;
    }

    function notify() {//更新观察者
        foreach ( $this->observers as $obs ) {
            $obs->update( $this );
        }
    }

    function handleLogin( $user, $pass, $ip ) {
        switch ( rand(1,3) ) {//根据随机结果通知观察者执行不同的观察者方法
            case 1:
                $this->setStatus( self::LOGIN_ACCESS, $user, $ip );
                $ret = true; break;
            case 2:
                $this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );
                $ret = false; break;
            case 3:
                $this->setStatus( self::LOGIN_USER_UNKNOWN, $user, $ip );
                $ret = false; break;
        }
        $this->notify();
        return $ret;
    }

    private function setStatus( $status, $user, $ip ) {
        $this->status = array( $status, $user, $ip );
    }
    function getStatus() {
        return $this->status;
    }

}
/**
 * 观察者
 */
interface Observer {//观察者接口
    function update( Observable $observer );
}

abstract class LoginObserver implements Observer {//抽象登陆类
    private $login;
    function __construct( Login $login ) {
        $this->login = $login;
        $login->attach( $this );
    }

    function update( Observable $observable ) {
        if ( $observable === $this->login ) {
            $this->doUpdate( $observable );
        }
    }

    abstract function doUpdate( Login $login );
}
/**
 * 具体的观察者类,这里即具体的登录类
 */
class SecurityMonitor extends LoginObserver {//安全登陆
    function doUpdate( Login $login ) {
        $status = $login->getStatus();
        if ( $status[0] == Login::LOGIN_WRONG_PASS ) {
            print __CLASS__.":\tsending mail to sysadmin\n";
        }
    }
}
class GeneralLogger  extends LoginObserver {//普通登陆
    function doUpdate( Login $login ) {
        $status = $login->getStatus();
        print __CLASS__.":\tadd login data to log\n";
    }
}
class PartnershipTool extends LoginObserver {//合作伙伴登陆
    function doUpdate( Login $login ) {
        $status = $login->getStatus();
        print __CLASS__.":\tset cookie if it matches a list\n";
    }
}

$login = new Login();
$SecurityMonitor = new SecurityMonitor( $login );
$GeneralLogger = new GeneralLogger( $login );
$PartnershipTool = new PartnershipTool( $login );
$login->detach( $PartnershipTool );
for ( $x=0; $x<3; $x++ ) {
    $login->handleLogin( "bob","mypass", '158.152.55.35' );
    print "\n";
}

 

SplSubject:被观察的对象。

/**
 * 被观察者
 */
class Login implements SplSubject {
    private $storage;
    const LOGIN_USER_UNKNOWN = 1;
    const LOGIN_WRONG_PASS   = 2;
    const LOGIN_ACCESS       = 3;

    function __construct() {
        $this->storage = new SplObjectStorage();
    }
    function attach( SplObserver $observer ) {
        $this->storage->attach( $observer );
    }

    function detach( SplObserver $observer ) {
        $this->storage->attach( $observer );
    }

    function notify() {
        foreach ( $this->storage as $obs ) {
            $obs->update( $this );
        }
    }

    function handleLogin( $user, $pass, $ip ) {
        switch ( rand(1,3) ) {
            case 1:
                $this->setStatus( self::LOGIN_ACCESS, $user, $ip );
                $ret = true;
                break;
            case 2:
                $this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );
                $ret = false;
                break;
            case 3:
                $this->setStatus( self::LOGIN_USER_UNKNOWN, $user, $ip );
                $ret = false;
                break;
        }
        $this->notify();
        return $ret;
    }

    private function setStatus( $status, $user, $ip ) {
        $this->status = array( $status, $user, $ip );
    }

    function getStatus() {
        return $this->status;
    }

}
/*
interface Observer {
    function update( SplSubject $observer );
}
*/
/**
 * 观察者
 */
abstract class LoginObserver implements SplObserver {
    private $login;
    function __construct( Login $login ) {
        $this->login = $login;
        $login->attach( $this );//这里执行了添加观察者的操作
    }

    function update( SplSubject $subject ) {
        if ( $subject === $this->login ) {
            $this->doUpdate( $subject );
        }
    }

    abstract function doUpdate( Login $login );
}

class SecurityMonitor extends LoginObserver {//安全登陆
    function doUpdate( Login $login ) {
        $status = $login->getStatus();
        if ( $status[0] == Login::LOGIN_WRONG_PASS ) {
            print __CLASS__.":安全登录<br>";
        }
    }
}

class GeneralLogger  extends LoginObserver {//普通登陆
    function doUpdate( Login $login ) {
        $status = $login->getStatus();
        print __CLASS__.":\t添加日志<br>";
    }
}

class PartnershipTool extends LoginObserver {//合作者登陆
    function doUpdate( Login $login ) {
        $status = $login->getStatus();
        print __CLASS__.":\t设置cookie <br>";
    }
}

$login = new Login();
new SecurityMonitor( $login );
new GeneralLogger( $login );
$PartnershipTool = new PartnershipTool( $login );
$login->detach( $PartnershipTool );
for ( $x=0; $x<1; $x++ ) {
    $login->handleLogin( "bob","mypass", '158.152.55.35' );
}

 

SplObserver接口,代表观察者的对象。SplSubject维护了一个特定的状态,当这个状态发生改变时,它就用notify()来通知之前注册的SplObserver对象,并且调用其相应的update方法。当然,在观察者类中,也需要有增加和删除观察者类的方法!这里是attach()和detach();

 

posted on 2014-06-22 15:00  color_story  阅读(188)  评论(0编辑  收藏  举报

导航