Unlike other SPL interfaces, I'm not finding that this pair of built-in interfaces has any intrinsic value other than to provide a template. It would be nice if Observer can automatically trigger its update()
method any time Subject properties are modified in any way, but this isn't the case. You have to set it up manually.
So to start, you want to separate contrlr
into separate SplSubject and SplObserver classes. Otherwise, for all intents and purposes, it's only notifying itself. model
can actually be the one that implements SplObserver in your case. But really, which class implements which interface would be up to you. Here's just an example of a basic usage...
class Observer implements SplObserver {
function update(SplSubject $subject) {
echo "notified: " . $subject->status . "\n";
}
}
class Subject implements SplSubject {
private $status;
private $observer;
function __construct(SplObserver $observer) {
$this->attach($observer);
}
function __destruct() {
$this->detach($this->observer);
}
function __get($prop) {
return $this->$prop;
}
function __set($prop, $val) {
$this->$prop = $val;
$this->notify();
}
function notify() {
$this->observer->update($this);
}
function attach(SplObserver $observer) {
$this->observer = $observer;
$this->__set("status", "attaching");
}
function detach(SplObserver $observer) {
$this->__set("status", "detaching");
$this->observer = null;
}
}
$observer = new Observer();
$sub1 = new Subject($observer);
$sub1->status = "testing";
$sub2 = new Subject($observer);
unset($sub2);
$sub1->status = "testing again";
Edit: You could have separate "model", "view", and "controller" classes all extending an abstract base class which implements SplSubject, SplObserver. This way they can all communicate with each other and complete your chart. But, the problem with that all centers around the notify()
method, where without the option to select which observer to notify, everyone gets the update message. The solution to allow communication between only one observer at a time would be to rip out the reference to the SPL interfaces entirely, and having your notify()
method take in a specific observer argument to send to.
Take the following example, which although has basically the same methods as the SPL interfaces blended together require, it doesn't implement those interfaces at all. Instead, each method takes in a variable number of arguments (expected to be other SubjectObserver objects)...
abstract class SubjectObserver {
protected $observer_list = array(); // assoc array of classname=>object
public $status;
function update(SubjectObserver $subject) {
echo get_class($this) . " notified by " . get_class($subject) . ": " . $subject->status . "\n";
}
function __destruct() {
foreach ($this->observer_list as $observer)
{ $this->detach($observer); }
}
function notify() {
$args = func_get_args();
foreach ($args as $arg)
{ $this->observer_list[get_class($arg)]->update($this); }
}
function attach() {
$args = func_get_args();
foreach ($args as $arg) {
$this->observer_list[get_class($arg)] = $arg;
$this->status = "attaching " . get_class($arg);
$this->notify($arg);
}
}
function detach() {
$args = func_get_args();
foreach ($args as $arg) {
$this->status = "detaching " . get_class($arg);
$this->notify($arg);
unset($this->observer_list[get_class($arg)]);
}
}
}
class model extends SubjectObserver {}
class view extends SubjectObserver {}
class controller extends SubjectObserver {}
// initialize
$model = new model();
$view = new view();
$controller = new controller();
$model->attach($controller, $view);
$view->attach($controller, $model);
$controller->attach($model, $view);
// process some input
$controller->status = "input received";
$controller->notify($model);
$model->status = "data filtered";
$model->notify($controller, $view);