Chapter 2: The Observer Pattern
Posted on August 5th, 2007 in PHP, PHP Design Patterns, Web Development | 4 Comments »
Here is the first stage of the Observer pattern, chapter 2 of Head First Design Patterns:
(On a side note, I hate the way Wordpress makes your post into what it thinks it should be. Wouldn’t you think that if I out text inside of a code that I wouldn’t want it auto-p’ed?)
< ?php // All class names are preprended with Sample_ to prevent namespace clashing // Need subject, concrete subject, observer and concrete observer interface Sample_Subject { public function registerObserver(Sample_Observer $o); public function removeObserver(Sample_Observer $o); public function notifyObservers(); } interface Sample_Observer { public function update($temp, $humidity, $pressure); } interface Sample_Display_Element { public function display(); } class Sample_Weather_Data implements Sample_Subject { private $observers; // In the example, this is object-cast, then built in the constructor private $temperature = 0.00; // This casts as float private $humidity = 0.00; private $pressure = 0.00; public function __construct() { $this->observers = array(); } public function registerObserver(Sample_Observer $o) { $this->observers[] = $o; } public function removeObserver(Sample_Observer $o) { // This has to be done totally different // Eventhough there is an ArrayObject in the SPL // It doesn't work like Java's foreach ($this->observers as $k => $v) { if ($v === $o) { unset($this->observers[$k]); } } } public function notifyObservers() { for ($i = 0; $i < count($this->observers); $i++) { $this->observers[$i]->update($this->temperature, $this->humidity, $this->pressure); } } public function measurementsChanged() { $this->notifyObservers(); } // The book uses temperature, but it needs to be temp for PHP public function setMeasurements($temp, $humidity, $pressure) { $this->temperature = (float) $temp; $this->humidity = (float) $humidity; $this->pressure = (float) $pressure; $this->measurementsChanged(); } } class Sample_Current_Conditions_Display implements Sample_Observer, Sample_Display_Element { private $temperature; private $humidity; private $weatherData; public function __construct(Sample_Weather_Data $weatherData) { $this->weatherData = $weatherData; $this->weatherData->registerObserver($this); } public function update($temperature, $humidity, $pressure) { $this->temperature = (float) $temperature; $this->humidity = (float) $humidity; $this->display(); } public function display() { echo "<p>Current conditions: " . $this->temperature . "F degrees and " . $this->humidity . "% humidity</p>"; } } class Sample_Statistics_Display implements Sample_Observer, Sample_Display_Element { private $maxTemp = 0.0; // Can't float and add F or it types to string private $minTemp = 200; private $tempSum = 0.0; // Again, no F private $numReadings = 0; private $weatherData; public function __construct(Sample_Weather_Data $weatherData) { $this->weatherData = $weatherData; $this->weatherData->registerObserver($this); } public function update($temp, $humidity, $pressure) { $this->tempSum += $temp; $this->numReadings++; if ($temp > $this->maxTemp) { $this->maxTemp = $temp; } if ($temp < $this->minTemp) { $this->minTemp = $temp; } $this->display(); } public function display() { echo "<p>Avg/Max/Min temperature = " . $this->tempSum / $this->numReadings . "/" . $this->maxTemp . "/" . $this->minTemp . "</p>"; } } class Sample_Forecast_Display implements Sample_Observer, Sample_Display_Element { private $currentPressure = 29.92; private $lastPressure; // Float private $weatherData; public function __construct(Sample_Weather_Data $weatherData) { $this->weatherData = $weatherData; $this->weatherData->registerObserver($this); } public function update($temp, $humidity, $pressure) { $this->lastPressure = $this->currentPressure; $this->currentPressure = $pressure; $this->display(); } public function display() { echo "<p>Forecast: "; if ($this->currentPressure > $this->lastPressure) { echo "Improving weather on the way!"; } elseif ($this->currentPressure == $this->lastPressure) { echo "More of the same."; } elseif ($this->currentPressure < $this->lastPressure) { // This could have been an else echo "Watch out for cooler, rainy weather."; } echo "</p>"; } } class Sample_Weather_Station { public $weatherData; public $currentConditionsDisplay; public $statisticsDisplay; public $forecastDisplay; public function __construct() { $this->weatherData = new Sample_Weather_Data(); $this->currentConditionsDisplay = new Sample_Current_Conditions_Display($this->weatherData); $this->statisticsDisplay = new Sample_Statistics_Display($this->weatherData); $this->forecastDisplay = new Sample_Forecast_Display($this->weatherData); $this->weatherData->setMeasurements(80.0, 65.0, 30.4); $this->weatherData->setMeasurements(82.0, 70.0, 29.2); $this->weatherData->setMeasurements(78.0, 90.0, 29.2); } } $ws = new Sample_Weather_Station(); ?>
Output:
Current conditions: 80F degrees and 65% humidity
Avg/Max/Min temperature = 80/80/80
Forecast: Improving weather on the way!
Current conditions: 82F degrees and 70% humidity
Avg/Max/Min temperature = 81/82/80
Forecast: Watch out for cooler, rainy weather.
Current conditions: 78F degrees and 90% humidity
Avg/Max/Min temperature = 80/82/78
Forecast: More of the same.