Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is SplSubject/SplObserver useful?

Tags:

The Standard PHP Library includes what some resources call a reference implementation of the Observer pattern, by way of the SplSubject and SplObserver classes. For the life of me, I can't figure out how these are very useful with no way to pass actual events or any other information along with the notifications:

class MySubject implements SplSubject {
    protected $_observers = [];

    public function attach(SplObserver $observer) {
        $id = spl_object_hash($observer);
        $this->_observers[$id] = $observer;
    }

    public function detach(SplObserver $observer) {
        $id = spl_object_hash($observer);

        if (isset($this->_observers[$id])) {
            unset($this->_observers[$id]);
        }
    }

    public function notify() {
        foreach ($this->_observers as $observer) {
            $observer->update($this);
        }
    }
}

class MyObserver implements SplObserver {
    public function update(SplSubject $subject) {
        // something happened with $subject, but what
        // was it???
    }
}

$subject = new MySubject();
$observer = new MyObserver();

$subject->attach($observer);
$subject->notify();

It seems like these interfaces are pretty much useless for any real world problem. Can someone enlighten me?


Edit:

Here's my biggest problem with the interface (although there are others):

public function update(SplSubject $subject, Event $event) { /* ... */ }

...nets the following fatal error:

PHP Fatal error:  Declaration of MyObserver::update() must be compatible with SplObserver::update(SplSubject $SplSubject)

Edit #2:

Making the additional parameters optional by giving them defaults prevents the fatal error and provides a way to pass context, making implementations worthwhile. I wasn't previously aware of this, so this pretty much answers my question. The solution is to pass your own event/message data, and check for its existance inside SplObserver::update().

like image 675
FtDRbwLXw6 Avatar asked Dec 08 '12 03:12

FtDRbwLXw6


1 Answers

It seems like these interfaces are pretty much useless for any real world problem. Can someone enlighten me?

Interface

While abstract classes let you provide some measure of implementation, interfaces are pure templates. An interface can only define functionality; it can never implement it. An interface is declared with the interface keyword. It can contain properties and method declarations, but not method bodies.

Interface Use case

For example if you want to your project should support different databases . so that you can change your database in future its better to use interfaces that contains property procedures in class file with out altering objects

By itself, interfaces are not very useful because You cannot create instances of interfaces but Interfaces are instrumental in enforcing object oriented design methodologies which in real sense makes your live easier as a programmer because the foremost incentive for object oriented programming is encapsulation (You don't care how a capability is implemented. You, as a programmer, are exposed only to the interface. This is also a good way to watch after the system architecture)

SplSubject & SplObserver

Orthogonality is a virtue , One of objectives as programmers should be to build components that can be altered or moved with minimal impact on other components.

If every change you make to one component necessitates a ripple of changes elsewhere in the codebase, the task of development can quickly become a spiral of bug creation and elimination.

There is no special feature of SplSubject and SplObserver because thy are both interface to implement the Observer Design Pattern.

Observer pattern

The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods. It is mainly used to implement distributed event handling systems

  • The Observer pattern defines an one-to-many dependency between a subject object and any number of observer objects so that when the subject object changes state, all its observer objects are notified and updated automatically.
  • The Observer pattern essentially allows an unlimited number of objects to observe or listen to events in the observed object (or subject) by registering themselves. After observers are registered to an event, the subject will notify them when the event is fired.
  • The subject handles this by storing an observer collection and iterating through it when the event occurs in order to notify each observer.
  • Observer Pattern registers observers with a subject.
  • You might have multiple observers. Subject must keep a list of registered observers and when event occurs it fires (provides notification) all registered observers.
  • Unregister also possible when we do not need any observer.

Example 1. Interest Rate Notification System for a Loan

$loan = new Loan("Mortage", "Citi Bank", 20.5);
$loan->attach(new Online());
$loan->attach(new SMS());
$loan->attach(new Email());

echo "<pre>";
$loan->setIntrest(17.5);

Output

Online    : Post online about modified Intrest rate of : 17.50
Send SMS  : Send SMS to premium subscribers : 17.50
Send Email: Notify mailing list : 17.50

Example 2. Simple User Register Monitor

$users = new Users();

new Audit($users);
new Logger($users);
new Security($users);

$users->addUser("John");
$users->addUser("Smith");
$users->addUser("Admin");

Output

Audit    : Notify Audit about John
Log      : User John Create at Wed, 12 Dec 12 12:36:46 +0100
Audit    : Notify Audit about Smith
Log      : User Smith Create at Wed, 12 Dec 12 12:36:46 +0100
Audit    : Notify Audit about Admin
Log      : User Admin Create at Wed, 12 Dec 12 12:36:46 +0100
Security : Alert trying to create Admin

Advantage of Observer Design Pattern: Main advantage is loose coupling between objects called observer and observable. The subject only know the list of observers it don’t care about how they have their implementation.All the observers are notified by subject in a single event call as Broadcast communication

Disadvantage of Observer Design Pattern:

  • The disadvantage is that the sometime if any problem comes, debugging becomes very difficult because flow of control is implicitly between observers and observable we can predict that now observer is going to fire and if there is chain between observers then debugging become more complex.
  • Another issue is Memory management when dealing with large observers

Common Classes

abstract class Observable implements SplSubject {
    protected $_observers = [];

    public function attach(SplObserver $observer) {
        $id = spl_object_hash($observer);
        $this->_observers[$id] = $observer;
    }

    public function detach(SplObserver $observer) {
        $id = spl_object_hash($observer);

        if (isset($this->_observers[$id])) {
            unset($this->_observers[$id]);
        }
    }

    public function notify() {
        foreach ( $this->_observers as $observer ) {
            $observer->update($this);
        }
    }
}



abstract class Observer implements SplObserver {
    private $observer;

    function __construct(SplSubject $observer) {
        $this->observer = $observer;
        $this->observer->attach($this);
    }
}

Load Example Classes

class Loan extends Observable {
    private $bank;
    private $intrest;
    private $name;

    function __construct($name, $bank, $intrest) {
        $this->name = $name;
        $this->bank = $bank;
        $this->intrest = $intrest;
    }

    function setIntrest($intrest) {
        $this->intrest = $intrest;
        $this->notify();
    }

    function getIntrest() {
        return $this->intrest;
    }
}

class Online implements SplObserver {

    public function update(SplSubject $loan) {
        printf("Online    : Post online about modified Intrest rate of : %0.2f\n",$loan->getIntrest());
    }
}

class SMS implements SplObserver {

    public function update(SplSubject $loan) {
        printf("Send SMS  : Send SMS to premium subscribers : %0.2f\n",$loan->getIntrest());
    }
}

class Email implements SplObserver {

    public function update(SplSubject $loan) {
        printf("Send Email: Notify mailing list : %0.2f\n",$loan->getIntrest());
    }
}

User Register Example Classes

class Users extends Observable {
    private $name;

    function addUser($name) {
        $this->name = $name;
        $this->notify();
    }

    function getName() {
        return $this->name;
    }
}
class Audit extends Observer {

    public function update(SplSubject $subject) {
        printf("Audit    : Notify Autify about %s\n", $subject->getName());
    }
}
class Logger extends Observer {

    public function update(SplSubject $subject) {
        printf("Log      : User %s Create at %s\n", $subject->getName(),date(DATE_RFC822));
    }
}
class Security extends Observer {
    public function update(SplSubject $subject) {
        if($subject->getName() == "Admin")
        {
            printf("Security : Alert trying to create Admin\n");
        }
    }
}
like image 176
Baba Avatar answered Oct 09 '22 12:10

Baba