Observer per due eventi indipendenti nella classe osservabile

3

Obiettivo del modello di osservatore: la classe di osservatori risponde ai cambiamenti dello stato della classe osservabile.

Problema. Ci sono diversi processi all'interno della classe osservabile che dovrebbero essere osservati. Questi eventi sono indipendenti e lo stato non è un valore singolo, ma piuttosto è un insieme di processi diversi.

Potrebbe essere una cattiva progettazione, la classe osservabile è troppo grande e dovrebbe essere divisa in più piccoli. Ma cosa succede se non possiamo refactoring di quella classe e dobbiamo osservarlo.

Domanda. Dovremmo avere un singolo osservatore per tutte le modifiche di stato (processi, eventi). O dovremmo creare un osservatore per ogni cambiamento di stato (processo, evento).

O magari applicare l'osservatore è una cattiva idea e dovrebbe essere usato un altro modello di design.

Esempio

L'esempio potrebbe sembrare artificiale per il modello di osservatore, ma dovrebbe essere visto come un esempio minimo.

Considera il modulo di ricerca dove l'utente può cercare:

  • autore
  • titolo
  • autore e titolo

Esiste la classe Reader responsabile per ottenere i parametri.

C'è un altro compito: memorizzare le parole che gli utenti stanno cercando nel file (logger semplificato). Vogliamo memorizzare i valori dell'autore in un file e i valori del titolo nell'altro. Non vogliamo aggiungere responsabilità per la classe Reader né estenderlo. Vogliamo osservare il suo stato interiore.

Una modifica di stato (evento) si verifica quando il parametro dell'autore viene letto dal modulo. Il secondo cambiamento di stato (evento) si verifica quando il parametro del titolo viene letto dal modulo. Questi eventi sono indipendenti. Non ci interessa tutto lo stato Reader (solo autore, solo titolo, titolo e autore). Siamo interessati solo agli stati dei lettori di parametri (autore letto o no) (titolo letto o no).

Un osservatore per tutti gli eventi

Quando viene notificato l'osservatore, non sa quale evento è successo. Viene introdotta la proprietà aggiuntiva whichState . L'osservatore deve controllare il suo valore ( SEARCH_BY_AUTHOR o SEARCH_BY_TITLE ) nel metodo update() .

Codice:

<?php class Reader implements \SplSubject { private $observers; public $author = null; public $title = null; // additional property to determine which state is observed public $whichState = null; // states const SEARCH_BY_AUTHOR = 'author'; const SEARCH_BY_TITLE = 'title'; public function __construct() { $this->observers = new \SplObjectStorage(); } public function attach(\SplObserver $observer) { $this->observers->attach($observer); } public function detach(\SplObserver $observer) { $this->observers->detach($observer); } public function notify() { foreach ($this->observers as $observer) { $observer->update($this); } } /* simulates reading from form */ public function readParams(array $params) { if (array_key_exists('author', $params) === true) { $this->author = $params['author']; $this->whichState = Reader::SEARCH_BY_AUTHOR; $this->notify(); } if (array_key_exists('title', $params) === true) { $this->title = $params['title']; $this->whichState = Reader::SEARCH_BY_TITLE; $this->notify(); } } } class BothParamsObserver implements \SplObserver { public function update(\SplSubject $subject) { if ($subject->whichState === Reader::SEARCH_BY_AUTHOR) { $author = $subject->author . "\r\n"; file_put_contents("authors.txt", $author, FILE_APPEND); } elseif ($subject->whichState === Reader::SEARCH_BY_TITLE) { $title = $subject->title . "\r\n"; file_put_contents("titles.txt", $title, FILE_APPEND); } } } $reader = new Reader(); $bothParamsObserver = new BothParamsObserver(); $reader->attach($bothParamsObserver); file_put_contents("authors.txt", ""); file_put_contents("titles.txt", ""); // simulate receiving forms $reader->readParams(array("title" => "PHP")); $reader->readParams(array("author" => "Zandstra")); $reader->readParams(array("title" => "PHP", "author" => "Zandstra")); ?>

Due osservatori, ciascuno per un evento

Gli osservatori sono duplicati all'interno della classe osservabile. C'è una collezione authorObservers e l'altra titleObservers . Esistono metodi attachAuthorObserver() , detachAuthorObserver() , notifyAuthorObservers() e ci sono metodi attachTitleObserver() , detachTitleObserver() , notifyTitlerObservers() .

Codice

<?php class Reader { private $authorObservers; private $titleObservers; public $author = null; public $title = null; public function __construct() { $this->authorObservers = new \SplObjectStorage(); $this->titleObservers = new \SplObjectStorage(); } public function attachAuthorObserver($observer) { $this->authorObservers->attach($observer); } public function attachTitleObserver($observer) { $this->titleObservers->attach($observer); } public function detachAuthorObserver($observer) { $this->authorObservers->detach($observer); } public function detachTitleObserver($observer) { $this->titleObservers->detach($observer); } public function notifyAuthorObservers() { foreach ($this->authorObservers as $observer) { $observer->update($this); } } public function notifyTitleObservers() { foreach ($this->titleObservers as $observer) { $observer->update($this); } } /* simulates reading from form */ public function readParams(array $params) { if (array_key_exists('author', $params) === true) { $this->author = $params['author']; $this->notifyAuthorObservers(); } if (array_key_exists('title', $params) === true) { $this->title = $params['title']; $this->notifyTitleObservers(); } } } class AuthorObserver { public function update($subject) { $author = $subject->author . "\r\n"; file_put_contents("authors.txt", $author, FILE_APPEND); } } class TitleObserver { public function update($subject) { $title = $subject->title . "\r\n"; file_put_contents("titles.txt", $title, FILE_APPEND); } } $reader = new Reader(); $authorObserver = new AuthorObserver(); $titleObserver = new TitleObserver(); $reader->attachAuthorObserver($authorObserver); $reader->attachTitleObserver($titleObserver); file_put_contents("authors.txt", ""); file_put_contents("titles.txt", ""); // simulate receiving forms $reader->readParams(array("title" => "PHP")); $reader->readParams(array("author" => "Zandstra")); $reader->readParams(array("title" => "PHP", "author" => "Zandstra"));

Aggiornamento: soluzione

Soluzione specifica basata sulla risposta di Robert Harvey.

Codice

<?php class Reader implements \SplSubject { private $observers; public $author = null; public $title = null; // additional property to determine which state is observed public $whichState = null; // states const SEARCH_BY_AUTHOR = 'author'; const SEARCH_BY_TITLE = 'title'; public function __construct() { $this->observers = new \SplObjectStorage(); } public function attach(\SplObserver $observer) { $this->observers->attach($observer); } public function detach(\SplObserver $observer) { $this->observers->detach($observer); } public function notify() { foreach ($this->observers as $observer) { $observer->update($this); } } /* simulates reading from form */ public function readParams(array $params) { if (array_key_exists('author', $params) === true) { $this->author = $params['author']; $this->whichState = Reader::SEARCH_BY_AUTHOR; $this->notify(); } if (array_key_exists('title', $params) === true) { $this->title = $params['title']; $this->whichState = Reader::SEARCH_BY_TITLE; $this->notify(); } } } class AuthorObserver implements \SplObserver { public function update(\SplSubject $subject) { if ($subject->whichState === Reader::SEARCH_BY_AUTHOR) { $author = $subject->author . "\r\n"; file_put_contents("authors.txt", $author, FILE_APPEND); } } } class TitleObserver implements \SplObserver { public function update(\SplSubject $subject) { if ($subject->whichState === Reader::SEARCH_BY_TITLE) { $title = $subject->title . "\r\n"; file_put_contents("titles.txt", $title, FILE_APPEND); } } } $reader = new Reader(); $authorObserver = new AuthorObserver(); $titleObserver = new TitleObserver(); $reader->attach($authorObserver); $reader->attach($titleObserver); file_put_contents("authors.txt", ""); file_put_contents("titles.txt", ""); // simulate receiving forms $reader->readParams(array("title" => "PHP")); $reader->readParams(array("author" => "Zandstra")); $reader->readParams(array("title" => "PHP", "author" => "Zandstra")); ?>     
posta mwloda 01.10.2015 - 23:54
fonte

1 risposta

1

Dovrebbe essere due osservatori.

L'oggetto Reader dovrebbe non avere metodi di collegamento e scollegamento specifici dell'osservatore. Piuttosto, dovrebbe avere due metodi per registrare e de-registrare gli oggetti dell'osservatore concreto in una singola raccolta di osservatori.

Wikipedia

Puoi ancora usare Reader:WhichState nei tuoi osservatori concreti per determinare se ogni osservatore concreto dovrebbe intraprendere qualsiasi azione.

    
risposta data 02.10.2015 - 00:22
fonte

Leggi altre domande sui tag