壁に耳がある観察者パターン

巴扎黑
リリース: 2016-11-12 13:56:28
オリジナル
1442 人が閲覧しました

全員がシステムにログインしている必要があります。ユーザー名とパスワードを確認した後、ログインが成功すると、ログイン エラーが発生した場合は、セキュリティ システムもエラーを記録する必要があります。関連するメールを管理者に送信するなど。これは、ログイン システムが多くの人によって監視されているようなもので、何か問題が発生すると、他のシステムがすぐにそれを認識します。次に、オブザーバー モードを使用してみます。クラス図は次のとおりです。

壁に耳がある観察者パターン
非常に単純なモードの実装コード:

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 at 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 対多の依存関係を定義します。これにより、オブジェクトの状態が変化するたびに、それに依存するすべてのオブジェクトが通知され、自動的に更新されます。オブザーバー パターンは 4 つの役割で構成されます:

1. 監視対象

オブザーバーを動的に追加およびキャンセルできる必要があります。これは通常、抽象クラスまたは実装クラスであり、オブザーバーとして実装する必要がある責任のみを果たし、オブザーバーを管理し、オブザーバーを渡します。

2. オブザーバー

メッセージを受信した後、オブザーバーは更新操作を実行し、受信した情報を処理します。

3. ConcreteSubject の特定のオブザーバブル

は、オブザーバブル自体のビジネス ロジックを定義し、どのイベントが通知されるかも定義します。

4. ConcreteObserver 固有のオブザーバー

各オブザーバーはメッセージを受信した後に異なる処理応答を持ち、各オブザーバーは独自の処理ロジックを持ちます。

オブザーバー パターンの利点

1. オブザーバーと監視対象の間には抽象的な結合があります

このように設計されているため、オブザーバーを追加するか監視されるオブジェクトを追加するかにかかわらず、拡張するのが非常に簡単で、すでに Java と Java で利用可能です。 php 実装の抽象レベルの定義は、システム拡張の観点からさらに便利です。

2. トリガーメカニズムを確立する

単一責任の原則によれば、各クラスは単一の責任を持ちます。では、現実の世界でそれぞれの単一責任を複雑な論理関係に結び付けるにはどうすればよいでしょうか?オブザーバー パターンは、ここでチェーン フォームを完全に実装できます

オブザーバー パターンの欠点

オブザーバー パターンは、開発効率と運用効率の問題を考慮する必要があります。 オブザーバーが 1 つ、複数の場合、開発とデバッグが多くなります。複雑であり、PHP ではメッセージ通知が順番に実行されます。オブザーバーが停止すると、全体の実行効率に影響します。この場合、一般的には非同期方式が考慮されます。多段トリガーの効率はさらに懸念されるため、設計時には注意してください。

オブザーバーモードの使用シナリオ

1. 関連する動作シナリオ。関係の動作は分離可能であり、「結合された」関係ではないことに注意してください

2. マルチレベルのイベントトリガーシナリオ

3. メッセージキュー処理メカニズムなどのシステム間メッセージ交換シナリオ


関連ラベル:
php
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート