Heim > Artikel > Backend-Entwicklung > Beobachtermuster, bei dem Wände Ohren haben
Jeder muss sich zuvor beim System angemeldet haben, um die Anmeldung erfolgreich durchzuführen. Wenn bei der Anmeldung ein Fehler auftritt, sollte das Sicherheitssystem den Fehler aufzeichnen Das E-Mail-System sollte auch relevante E-Mails an das Management-Mitglied senden. Dies ist so, als ob das Anmeldesystem von vielen Leuten überwacht wird. Sobald es Probleme gibt, werden andere Systeme sofort davon erfahren. Versuchen Sie dann, den Beobachtermodus zu verwenden:
Ein sehr einfacher Modus, der Implementierungscode:
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('XXX','XXX','127.0.0.1'); ?>
Ergebnis ausführen, wenn ein Fehler auftritt:
SecurityMonitor: XXX konnte sich nicht bei 127.0.0.1 anmelden [Fertig in 0,1 s]
Sie können das sehen login-Objekt im Code Fügen Sie aktiv die SecurityMonitor-Objektbeobachtung hinzu. Um Login::getStatus() aufzurufen, muss die SecurityMonitor-Klasse auf diese Weise weitere Informationen kennen. Obwohl der Aufruf für ein ObServable-Objekt erfolgt, gibt es keine Garantie dafür, dass das Objekt auch ein Login-Objekt ist. Um dieses Problem zu lösen, gibt es einen Weg: Halten Sie die ObServable-Schnittstelle zeitweise generisch, und die ObServer-Klasse ist dafür verantwortlich, sicherzustellen, dass ihre Körper den richtigen Typ haben. Sie können sich sogar selbst zum Thema hinzufügen. Das Klassendiagramm lautet wie folgt:
Der Implementierungscode lautet wie folgt:
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( 'XXX', 'XXX', '127.0.0.1' ); ?>
Das laufende Ergebnis ist das gleiche wie das obige Beispiel
in Nach PHP5 bietet die integrierte SPL-Erweiterung native Unterstützung für das Beobachtermuster. Nachdem Sie das obige Beispiel durch SPL verbessert haben:
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( 'XXX', 'XXX', '127.0.0.1' ); ?>
Der Code wurde geschrieben, aber Sie müssen noch einige Theorien verstehen.
Definition des Beobachtermusters
Definieren Sie eine Eins-zu-viele-Abhängigkeitsbeziehung zwischen Objekten, sodass jedes Mal, wenn ein Objekt seinen Zustand ändert, alle davon abhängigen Objekte benachrichtigt werden und automatisch erneuert werden. Das Beobachtermuster besteht aus vier Rollen:
1. Beobachteter Beobachter
definiert die Verantwortlichkeiten, die der Beobachter umsetzen muss, und es muss in der Lage sein, Beobachter dynamisch hinzuzufügen und zu löschen. Im Allgemeinen handelt es sich um eine abstrakte Klasse oder eine Implementierungsklasse, die nur die Verantwortlichkeiten erfüllt, die als Beobachter implementiert werden müssen, Beobachter verwaltet und Beobachter weitergibt.
2. Beobachter
Nach Erhalt der Nachricht führt der Beobachter einen Aktualisierungsvorgang durch und verarbeitet die empfangenen Informationen.
3. Der spezifische Beobachter von ConcreteSubject
definiert die eigene Geschäftslogik des Beobachters und definiert auch, über welche Ereignisse benachrichtigt werden soll.
4. ConcreteObserver-spezifische Beobachter
Jede Beobachtung hat eine andere Verarbeitungsreaktion nach dem Empfang der Nachricht, und jeder Beobachter hat seine eigene Verarbeitungslogik.
Vorteile des Beobachtermusters
1. Es gibt eine abstrakte Kopplung zwischen dem Beobachter und dem Beobachteten
Bei diesem Design spielt es keine Rolle, ob der Beobachter oder das Beobachtete hinzugefügt wird Es ist sehr einfach zu erweitern und die Definition von Abstraktionsebenen wurde in Java und PHP implementiert, was die Systemerweiterung noch komfortabler macht.
2. Richten Sie einen Auslösemechanismus ein
Nach dem Prinzip der Einzelverantwortung hat jede Klasse eine einzige Verantwortung. Wie kann man also jede einzelne Verantwortung in einer komplexen logischen Beziehung in der realen Welt verbinden? Das Beobachtermuster kann hier die Kettenform perfekt implementieren
Nachteile des Beobachtermusters
Das Beobachtermuster muss Entwicklungseffizienz und Betriebseffizienz berücksichtigen ist ein Problem. Bei einem Beobachter und mehreren Beobachtern wird die Entwicklung und das Debuggen komplizierter. Darüber hinaus werden Nachrichtenbenachrichtigungen nacheinander ausgeführt. Wenn ein Beobachter hängen bleibt, wirkt sich dies auf die Gesamtausführungseffizienz aus. In diesem Fall kommen im Allgemeinen asynchrone Methoden in Betracht. Noch besorgniserregender ist die Effizienz der mehrstufigen Auslösung. Achten Sie daher beim Entwurf darauf.
Nutzungsszenarien des Beobachtermodus
1. Es ist zu beachten, dass das Beziehungsverhalten trennbar und keine „kombinierte“ Beziehung ist
2. Mehrstufige Ereignisauslöseszenarien
3. Systemübergreifende Nachrichtenaustauschszenarien, z. B. Nachricht Warteschlangenverarbeitungsmechanismus