Home  >  Article  >  Backend Development  >  Observer Pattern where walls have ears

Observer Pattern where walls have ears

巴扎黑
巴扎黑Original
2016-11-12 13:56:281386browse

Everyone must have logged in to the system. After verifying the username and password, the login is successful. The log system should record the login. If there is a login error, the security system should record the error. The email system should also send relevant emails to the administrator. etc. This is like the login system being monitored by many people. Once there is any trouble, other systems will immediately learn about it. Then try using the observer mode. The class diagram is as follows:

Observer Pattern where walls have ears
Very simple mode, implementation code:

Php code

<?php  
interface Observable{  
    function attach( Observer $observer );  
    function detach( Observer $observer );  
    function notify();  
}  
  
  
class login implements Observable{  
    const LOGIN_USER_UNKNOW = 1;  
    const LOGIN_WRONG_PASS = 2;  
    const LOGIN_ACCESS = 3;  
    private $status = array();  
    private $observers = array();  
  
    public function setStatus( $status, $user, $ip ) {  
        $this->status = array( $status, $user, $ip );  
    }  
    public function getStatus() {  
        return $this->status;  
    }  
    public function handleLogin( $user, $pass, $ip ) {  
        switch ( mt_rand( 1, 3 ) ) {  
        case 1:  
            $this->setStatus( self::LOGIN_USER_UNKNOW, $user, $ip );  
            $ret = false;  
            break;  
        case 2:  
            $this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );  
            $ret = false;  
            break;  
        case 3:  
            $this->setStatus( self::LOGIN_ACCESS, $user, $ip );  
            $ret = true;  
            break;  
        }  
        $this->notify();  
        return $ret;  
    }  
  
  
    public function attach( Observer $observer ) {  
        $this->observers[] = $observer;  
    }  
  
    public function detach( Observer $observer ) {  
        $newObservers = array();  
        foreach ( $this->observers as $obs ) {  
            if ( $obs !== $observer )  
                $newObservers[] = $obs;  
        }  
        $this->observers = $newObservers;  
    }  
  
    public function notify() {  
        foreach ( $this->observers as $obs ) {  
            $obs->update( $this );  
        }  
    }  
}  
  
interface Observer{  
    function update( Observable $observable );  
}  
  
class SecurityMonitor implements Observer{  
    function update( Observable $observable ) {  
        $status = $observable->getStatus();  
        if($status[0] == Login::LOGIN_WRONG_PASS){  
            echo __CLASS__.":".$status[1]."于".$status[2]."登录失败";  
        }  
    }  
}  
  
$login = new Login();  
$login->attach(new SecurityMonitor());  
$login->handleLogin(&#39;XXX&#39;,&#39;XXX&#39;,&#39;127.0.0.1&#39;);  
?>


Running results when an error occurs:

SecurityMonitor:XXX at 127.0. 0.1 Login failed [Finished in 0.1s]

You can see in the code that the login object actively adds the SecurityMonitor object for observation. In this way, to call Login::getStatus(), the SecurityMonitor class must know more information. Although the call occurs on an ObServable object, there is no guarantee that the object is also a Login object. To solve this problem, there is a way: keep the ObServable interface generic intermittently, and the ObServer class is responsible for ensuring that their bodies are of the correct type. They can even add themselves to the subject. The class diagram is as follows:

Observer Pattern where walls have ears
The implementation code is as follows:

Php code

<?php  
interface Observable{  
    function attach( Observer $observer );  
    function detach( Observer $observer );  
    function notify();  
}  
  
  
class login implements Observable{  
    const LOGIN_USER_UNKNOW = 1;  
    const LOGIN_WRONG_PASS = 2;  
    const LOGIN_ACCESS = 3;  
    private $status = array();  
    private $observers = array();  
  
    public function setStatus( $status, $user, $ip ) {  
        $this->status = array( $status, $user, $ip );  
    }  
    public function getStatus() {  
        return $this->status;  
    }  
    public function handleLogin( $user, $pass, $ip ) {  
        switch ( mt_rand( 1, 3 ) ) {  
        case 1:  
            $this->setStatus( self::LOGIN_USER_UNKNOW, $user, $ip );  
            $ret = false;  
            break;  
        case 2:  
            $this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );  
            $ret = false;  
            break;  
        case 3:  
            $this->setStatus( self::LOGIN_ACCESS, $user, $ip );  
            $ret = true;  
            break;  
        }  
        $this->notify();  
        return $ret;  
    }  
  
  
    public function attach( Observer $observer ) {  
        $this->observers[] = $observer;  
    }  
  
    public function detach( Observer $observer ) {  
        $newObservers = array();  
        foreach ( $this->observers as $obs ) {  
            if ( $obs !== $observer )  
                $newObservers[] = $obs;  
        }  
        $this->observers = $newObservers;  
    }  
  
    public function notify() {  
        foreach ( $this->observers as $obs ) {  
            $obs->update( $this );  
        }  
    }  
}  
  
interface Observer{  
    function update( Observable $observable );  
}  
//以上代码和上例是一样的  
abstract class LoginObserver implements Observer{  
    private $login;  
    public function __construct( Login $login ) {  
        $this->login = $login;  
        $login->attach( $this );  
    }  
  
    public function update( Observable $observable ) {  
        if ( $this->login === $observable )  
            $this->doUpdate( $observable );  
    }  
    abstract function doUpdate( Login $login );  
}  
  
class SecurityMonitor extends LoginObserver{  
    public function doUpdate( Login $login ) {  
        $status = $login->getStatus();  
        if ( $status[0] == Login::LOGIN_WRONG_PASS )  
            echo __CLASS__.":".$status[1]."于".$status[2]."登录失败";  
    }  
}  
  
$login = new Login();  
new SecurityMonitor($login);//<strong>此外login对象是被动被观察的</strong>  
$login->handleLogin( &#39;XXX&#39;, &#39;XXX&#39;, &#39;127.0.0.1&#39; );  
?>

The running result is the same as the above example

After php5, the built-in SPL extension provides native support for the observer mode. After improving the above example through SPL:

Php code

<?php  
class login implements SplSubject{  
    const LOGIN_USER_UNKNOW = 1;  
    const LOGIN_WRONG_PASS = 2;  
    const LOGIN_ACCESS = 3;  
    private $status = array();  
    // private $observers = array();  
    private $storage;  
    public function __construct() {  
        $this->storage = new SplObjectStorage();  
    }  
    public function setStatus( $status, $user, $ip ) {  
        $this->status = array( $status, $user, $ip );  
    }  
    public function getStatus() {  
        return $this->status;  
    }  
    public function handleLogin( $user, $pass, $ip ) {  
        switch ( mt_rand( 1, 3 ) ) {  
        case 1:  
            $this->setStatus( self::LOGIN_USER_UNKNOW, $user, $ip );  
            $ret = false;  
            break;  
        case 2:  
            $this->setStatus( self::LOGIN_WRONG_PASS, $user, $ip );  
            $ret = false;  
            break;  
        case 3:  
            $this->setStatus( self::LOGIN_ACCESS, $user, $ip );  
            $ret = true;  
            break;  
        }  
        $this->notify();  
        return $ret;  
    }  
  
  
    public function attach( SplObserver $observer ) {  
        $this->storage->attach( $observer );  
    }  
  
    public function detach( SplObserver $observer ) {  
        $this->storage->detach( $observer );  
    }  
  
    public function notify() {  
        foreach ( $this->storage as $obs ) {  
            $obs->update( $this );  
        }  
    }  
}  
  
abstract class LoginObserver implements SplObserver{  
    private $login;  
    public function __construct( Login $login ) {  
        $this->login = $login;  
        $login->attach( $this );  
    }  
  
    public function update( SplSubject $subject ) {  
        if ( $this->login === $subject )  
            $this->doUpdate( $subject );  
    }  
    abstract function doUpdate( Login $login );  
}  
  
class SecurityMonitor extends LoginObserver{  
    public function doUpdate( Login $login ) {  
        $status = $login->getStatus();  
        if ( $status[0] == Login::LOGIN_WRONG_PASS )  
            echo __CLASS__.":".$status[1]."于".$status[2]."登录失败";  
    }  
}  
  
$login = new Login();  
new SecurityMonitor( $login );  
$login->handleLogin( &#39;XXX&#39;, &#39;XXX&#39;, &#39;127.0.0.1&#39; );  
?>

The code has been written, but you still need to understand some theory.

Definition of Observer Pattern

Define a one-to-many dependency relationship between objects, so that whenever an object changes state, all objects that depend on it will be notified and automatically updated. The observer pattern consists of four roles:

1. Subject Observed

Defines the responsibilities that the observed must implement. It must be able to dynamically add and cancel observers. It is generally an abstract class or an implementation class, which only fulfills the responsibilities that must be implemented as an observer, manages observers and passes observers.

2. Observer

After receiving the message, the observer will perform an update operation and process the received information.

3. ConcreteSubject’s specific observable

defines the observable’s own business logic and also defines which events will be notified.

4. ConcreteObserver specific observer

Each observation has a different processing response after receiving the message, and each observer has its own processing logic.

Advantages of the observer pattern

1. There is an abstract coupling between the observer and the observed

Designed in this way, it is very easy to expand whether adding observers or observed objects, and it is already available in java and php The definition of the abstract level of implementation is even more convenient in terms of system expansion.

2. Establish a trigger mechanism

According to the single responsibility principle, each class has a single responsibility. So how to connect each single responsibility into a complex logical relationship in the real world? The observer pattern can perfectly realize the chain form here

Disadvantages of the observer pattern

The observer pattern needs to consider the issues of development efficiency and operating efficiency. One observer, multiple observers, development and Debugging will be more complicated, and in PHP, message notifications are executed sequentially. If an observer is stuck, it will affect the overall execution efficiency. In this case, asynchronous methods are generally considered. The efficiency of multi-stage triggering is even more worrying, so pay attention when designing.

Usage scenarios of observer mode

1. Associated behavior scenarios. It should be noted that the relationship behavior is detachable, not a "combined" relationship

2. Multi-level event triggering scenarios

3. Cross-system message exchange scenarios, such as message queue processing mechanism


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn