>  기사  >  백엔드 개발  >  벽에 귀가 있는 관찰자 패턴

벽에 귀가 있는 관찰자 패턴

巴扎黑
巴扎黑원래의
2016-11-12 13:56:281423검색

모두가 이전에 시스템에 로그인한 적이 있어야 합니다. 로그인에 오류가 있으면 로그 시스템에 로그인이 기록되어야 하며, 이메일 시스템은 회원에게도 관련 이메일을 보내야 합니다. 잠시만요. 이는 많은 사람들이 로그인 시스템을 모니터링하는 것과 같습니다. 문제가 발생하면 다른 시스템이 즉시 이에 대해 알게 됩니다. 그런 다음 관찰자 모드를 사용해 보세요.

벽에 귀가 있는 관찰자 패턴
매우 간단한 모드, 구현 코드:

Php 코드

<?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;);  
?>


오류 발생 시 실행 결과:

SecurityMonitor: XXX가 127.0.0.1에 로그인하지 못했습니다. [0.1초 만에 완료]

코드의 로그인 개체 SecurityMonitor 개체 관찰을 적극적으로 추가합니다. 이런 방식으로 Login::getStatus()를 호출하려면 SecurityMonitor 클래스가 더 많은 정보를 알아야 합니다. 호출이 ObServable 객체에서 발생하더라도 객체가 Login 객체이기도 하다는 보장은 없습니다. 이 문제를 해결하기 위한 방법이 있습니다. ObServable 인터페이스를 간헐적으로 일반으로 유지하고 ObServer 클래스는 해당 본문이 올바른 유형인지 확인하는 일을 담당합니다. 주제에 자신을 추가할 수도 있습니다. 클래스 다이어그램은 다음과 같습니다.

벽에 귀가 있는 관찰자 패턴
구현 코드는 다음과 같습니다.

Php 코드

<?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; );  
?>

실행 결과는 다음과 같습니다. 위의 예

는 php5 이후 내장 SPL 확장이 관찰자 패턴에 대한 기본 지원을 제공합니다. SPL을 통해 위의 예를 개선한 후:

Php 코드

<?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; );  
?>

코드는 작성되었지만 여전히 몇 가지 이론을 이해해야 합니다.

관찰자 패턴 정의

객체의 상태가 변경될 때마다 이에 의존하는 모든 객체가 알림을 받고 자동으로 갱신되도록 객체 간의 일대다 종속 관계를 정의합니다. 관찰자 패턴은 네 가지 역할로 구성됩니다.

1. 관찰된 주체

는 관찰자가 구현해야 하는 책임을 정의하며, 관찰자를 동적으로 추가하고 취소할 수 있어야 합니다. 일반적으로 옵저버로서 구현해야 하는 책임만 완료하고 옵저버를 관리하며 옵저버를 통과시키는 추상 클래스 또는 구현 클래스입니다.

2. 관찰자

메시지를 받은 후 관찰자는 업데이트 작업을 수행하고 수신된 정보를 처리합니다.

3. ConcreteSubject의 특정 관찰자

는 관찰자 자신의 비즈니스 로직을 정의하고, 어떤 이벤트를 통보받을지도 정의합니다.

4. ConcreteObserver 특정 관찰자

각 관찰은 메시지를 받은 후 다른 처리 응답을 가지며 각 관찰자는 고유한 처리 논리를 갖습니다.

관찰자 패턴의 장점

1. 관찰자와 관찰자 사이에 추상적 결합이 있습니다.

이 디자인을 사용하면 관찰자 또는 관찰자가 추가됩니다. , 확장이 매우 쉽고, 추상화 수준의 정의가 Java 및 PHP로 구현되어 시스템 확장이 더욱 편리해졌습니다.

2. 트리거 메커니즘 설정

단일 책임 원칙에 따라 각 클래스에는 단일 책임이 있는데, 현실 세계에서 각 단일 책임을 어떻게 복잡한 논리적 관계로 연결할 수 있을까요? 여기서 옵저버 패턴은 체인 형태를 완벽하게 구현할 수 있습니다

옵저버 패턴의 단점

옵저버 패턴은 개발 효율성과 운영 효율성을 고려해야 합니다. 문제는 관찰자가 하나이고 관찰자가 여러 명일 경우 개발 및 디버깅이 더 복잡해지며, PHP에서는 관찰자가 중단되면 전체 실행 효율성에 영향을 미칩니다. 이 경우 일반적으로 비동기식 방법이 고려됩니다. 다단계 트리거링의 효율성은 더욱 걱정스럽기 때문에 설계 시 주의를 기울이십시오.

관찰자 모드 사용 시나리오

1. 관계 동작은 "결합된" 관계가 아니라 분리 가능하다는 점에 유의해야 합니다

2. 다단계 이벤트 트리거 시나리오

3. 메시지와 같은 시스템 간 메시지 교환 시나리오 대기열 처리 메커니즘


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.