Thoughts, rants and commentary from a husband, father of five and professional web geek

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.

Back to top