Observer Design Pattern in PHP

Observer Design Pattern is one of the behavioral design patterns in Software Engineering. The observer design pattern is useful when you are concerned with the state of an object and want to be notified of any changes. In the observer pattern, the object that monitors the state of another object is called the Observer, and the tracked object is called the Subject.

What is the Observer Design Pattern?

Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

Subject includes a list of observers to notify any changes in its state, so it must provide methods by which observers can attach and detach their own. The subject also includes a method for notifying all observers of any changes and can either send the update while notifying the observer or provide another method to receive the update.

Observer must have a method for setting the object to be tracked and another method that will be used by the Subject to inform them about any updates.

The observer design pattern can basically be represented in UML as:

A Demonstration of Observer Design Pattern in UML

Observer Design Pattern is also called as publish-subscribe pattern.

As an example, Model-View-Controller (MVC) frameworks use the Observer Design Pattern where Model is the Subject and Views are observers that can attach to get notified of any change to the model.

Observer Design Pattern PHP Example

Demonstration of our example scoreboard application in UML:

UML Diagram of Scoreboard Application Example

Based on the requirements of Subject, here is the base Subject interface that defines the contract methods to be implemented by any concrete subject.

<?php

namespace App;

abstract class Subject
{
protected $observers = [];

public function attach(Observer $observer)
{
$this->observers[] = $observer;
}

public function detach(Observer $observer)
{
$this->observers = array_diff($this->observers, [$observer]);
}

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

}

Next, we will create a contract for Observer, there will be a method to attach the Subject to the observer and another method to be used by Subject to notify of any change.

<?php

namespace App;

interface Observer
{
public function update();
}

Now our contracts are ready, let’s proceed with the concrete implementation of Match in our example.

<?php

namespace App;

class Match extends Subject
{
private $homeTeam;
private $awayTeam;
private $homeTeamScore;
private $awayTeamScore;

/**
* Match constructor.
*
@param $homeTeam
*
@param $awayTeam
*/
public function __construct($homeTeam, $awayTeam)
{
$this->homeTeam = $homeTeam;
$this->awayTeam = $awayTeam;
$this->homeTeamScore = 0;
$this->awayTeamScore = 0;
}

/**
* Increments Home Team's Score
*/
public function incrementHomeTeamScore()
{
$this->homeTeamScore++;
$this->notify();
}

/**
* Increments Away Team's Score
*/
public function incrementAwayTeamScore()
{
$this->awayTeamScore++;
$this->notify();
}

/**
*
@return mixed
*/
public function getHomeTeam()
{
return $this->homeTeam;
}

/**
*
@return mixed
*/
public function getAwayTeam()
{
return $this->awayTeam;
}

/**
*
@return int
*/
public function getHomeTeamScore()
{
return $this->homeTeamScore;
}

/**
*
@return int
*/
public function getAwayTeamScore()
{
return $this->awayTeamScore;
}


}

Lastly, here is the concrete implementation of Scoreboard that will watch over the updates of matches.

<?php

namespace App;

class Scoreboard implements Observer
{
private $matches = [];
private $name;

/**
* ScoreBoard constructor.
*
@param $name
*/
public function __construct($name)
{
$this->name = $name;
}

/**
*
@param Match $match
*/
public function addMatch(Match $match)
{
$match->attach($this);
$this->matches[] = $match;
}

/**
*
@param Match $match
*/
public function removeMatch(Match $match)
{
$match->detach($this);
$this->matches = array_diff($this->matches, [$match]);
}

/**
* Display the Scoreboard
*/
public function show()
{
echo "Scoreboard: $this->name" . PHP_EOL;
foreach ($this->matches as $match) {
echo str_pad($match->getHomeTeam(), 20, " ", STR_PAD_LEFT);
echo " " . $match->getHomeTeamScore();
echo " - ";
echo $match->getAwayTeamScore() . " ";
echo $match->getAwayTeam() . PHP_EOL;
}
}

/**
* Run when a match is updated.
*/
public function update()
{
echo "A match score has changed." . PHP_EOL;
$this->show();
}
}

Finally, here is a simple test program to demonstrate its functionality.

<?php

require __DIR__ . '/vendor/autoload.php';

$match1 = new \App\Match(
"Galatasaray",
"Fenerbahçe"
);

$match2 = new \App\Match(
"Barcelona",
"Real Madrid"
);

$match3 = new \App\Match(
"Milan",
"Inter"
);

$scoreBoard1 = new \App\Scoreboard("SB1");
$scoreBoard1->addMatch($match1);
$scoreBoard1->addMatch($match2);
$scoreBoard1->show();

$scoreBoard2 = new \App\Scoreboard("SB2");
$scoreBoard2->addMatch($match1);
$scoreBoard2->addMatch($match3);
$scoreBoard2->show();

$match1->incrementHomeTeamScore();
$match2->incrementAwayTeamScore();
$match3->incrementAwayTeamScore();

When we run the above program, we get the following output.

Scoreboard: SB1
Galatasaray
0 - 0 Fenerbahçe
Barcelona
0 - 0 Real Madrid
Scoreboard: SB2
Galatasaray
0 - 0 Fenerbahçe
Milan
0 - 0 Inter
A match score has changed
.
Scoreboard: SB1
Galatasaray
1 - 0 Fenerbahçe
Barcelona
0 - 0 Real Madrid
A match score has changed
.
Scoreboard: SB2
Galatasaray
1 - 0 Fenerbahçe
Milan
0 - 0 Inter
A match score has changed
.
Scoreboard: SB1
Galatasaray
1 - 0 Fenerbahçe
Barcelona
0 - 1 Real Madrid
A match score has changed
.
Scoreboard: SB2
Galatasaray
1 - 0 Fenerbahçe
Milan
0 - 1 Inter

That’s all for the Observer Design Pattern in PHP, I hope you enjoyed it. Please feel free to leave comments and share the article with others.

Go to Github repository of example application here: https://github.com/bariscimen/ObserverPatternPHP

PhDc, Senior Software Engineer