Eventgesteuerte Programmierung vs. feste Kopplung der Komponenten
Bei der herkömmlichen Art der Programmierung stehen Aktion und Reaktion in festem Zusammenhang. Eine Aktion wird meistens durch den Benutzer der Website ausgelöst und Ihre PHP-Anwendung reagiert darauf. Die beiden Aktionen sind über in PHP implementierte Programmlogik fest miteinander verknüpft, wozu in den meisten Fällenif/else
– oder switch/case
-Konstrukte verwendet werden. Um nun die Reaktion der Applikation zu verändern, müssen Sie den Code innerhalb der if/else
-Blöcke verändern oder sogar neue Blöcke hinzufügen.
Stellen Sie sich zum Beispiel vor, Sie implementieren eine Benutzerauthentifizierung für eine Website. Dabei werden im HTTP-Request der Name sowie das Passwort des Benutzers, der sich anmelden möchte, an Ihre Anwendung übertragen.
Diese versucht dann die beiden Parameter mit den Kundendaten, die zum Beispiel in einer Datenbank gespeichert sind, zu vergleichen. Stimmen die Daten überein, so ist der Kunde authentifiziert und kann die Applikation verwenden, wurde das Passwort falsch übergeben, so zeigen Sie dem Kunden eine Fehlermeldung an. Auf dem klassischen Weg haben Sie diesen Code sicher schon in mehreren Anwendungen implementiert.
Was aber nun, wenn eine neue Anforderung besagt, dass bei Fehlversuchen ein Log geschrieben werden soll, um später analysieren zu können, ob jemand versucht, unberechtigten Zugriff auf Ihre Anwendung zu erhalten? In diesem Fall würden Sie sicher einfach in dem Block der if/else
-Anweisung, der ausgeführt wird, wenn die Daten nicht übereinstimmen, den zusätzlichen Code einfügen, der den Logeintrag erzeugt. Sie müssen also funktionierenden Code erneut anpassen, was zu Fehlern führen kann. Eine Woche später stellen Ihre Auftraggeber fest, dass sie auch gerne eine Logdatei mit den erfolgreichen Logins hätten, um eine Verteilung der erfolgreichen zu erfolglosen Versuchen erstellen zu können. Diese Funktion integrieren Sie auf dieselbe Art und Weise und schon wieder müssen Sie funktionierenden Quellcode verändern. Dieser Kreislauf könnte immer so weiter gehen, denn Ihre Auftraggeber könnten noch weitere Anforderungen an das System haben, wie zum Beispiel:
- Bei erfolgreichem Login soll ein Cookie gesetzt werden, mit dem der Kunde bei einem späteren Besuch erkannt werden kann.
- Von jeder IP-Adresse sollen innerhalb von 10 Minuten nur drei erfolglose Login-Versuche erlaubt werden.
- Es soll möglich sein, für ausgewählte Kunden den Login komplett zu sperren.
Das Subject/Observer-Pattern
Wenn Sie bereits schon einmal mit dem Subject/Observer-Pattern gearbeitet haben, so kommt Ihnen dieser Wunsch vielleicht bekannt vor. Bei diesem Entwurfsmuster übernimmt ein Objekt die Rolle des Subjekts und steht im Zentrum des Interesses. An diesem Objekt können sich weitere Objekte als so genannte Observer registrieren. Ändert sich der Status des Subjektes, so informiert dies alle Observer über den neuen Status. Für die Login-Funktionalität bedeutet dies, dass die Klasse, die sich um die eigentliche Authentifizierung kümmert, als Subjekt agiert, das nur drei Stati kennt: keine Authentifizierung versucht, Authentifizierung erfolgreich und Authentifizierung fehlgeschlagen. Die Observer sind dann die neuen zusätzlichen Funktionen wie das Schreiben der Log-Dateien. Sobald nun ein Login-Versuch durchgeführt wurde, ändert sich der Status des Authentifizierungs-Objektes und es informiert die weiteren Funktionen über den erfolgreichen oder erfolglosen Login-Versuch. Abbildung 1 zeigt den vereinfachten Aufbau der Applikation.
Abb. 1: Architektur auf Basis des Subject/Observer-Musters