Design Patterns – Observer

The Observer is a Behavioral Design Pattern often known as Dependents or the Publish-Subscriber pattern where the subject would be the publisher and observer would be the subscriber.

It defines and maintains a dependency between objects. It is used to keep consistency, or where you don’t know who needs to be notified when your Subject changes.

It will be found in a lot of frameworks because is one of the most commonly used design patterns, I know, I might have said it before, but there are many chances that you have seen and even used it before.

The easiest way that I use to remember this pattern is by associating it with Events (Publish-Subscriber) or a News Station.

You can implement this pattern in two different ways, using a Push Style or Pull Style, we will review this and their main differences, but first, let’s check the intent of this pattern.


Intent

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


Diagram


Subject – Also known as Observable: Is who publishes an update to all his observers through his notify method, is important to know that this doesn’t specify a quantity of how many objects could be attached to a subject also, provides an interface that allows attach and detach observers to it.

Observer: Defines an updating interface to the objects who must be notified when the Subject has changed.

ConcreteSubject: Stores state of interest to observers and will send a notification to all its observers when its state changes

ConcreteObserver: Maintains a reference to a ConcreteSubject and will use and keep the ConcreteSubject states, to achieve it will implement the Observer updating interface to keep its state consistent with the subjects.


Observer, Push Style vs Pull Style

Firstly, we’ll need to change our Diagram to understand how to push style works. To achieve this we need to change our Observer interface to accept a parameter, that could be our subjectState property or it could also be another object that should be passed here. With this, we also have to change our Notify Method at Subject class to pass this parameter to Concrete Observers.

As you can observe (lol) the push method tells us to send the subject through our notify method meanwhile the Pull Style will send a notice telling that something has changed in the subject and each object(Observer) should call the subject to get his state, using the GetState() method as we have seen in the first image.


Which one is better?

To be honest, it is a choice of your team, I have seen this implemented in both ways and both work fine but if I’d to choose one, I’ll go with Pull Style that seems that we have less coupling when using it, but is also totally fine for me, creating a subjectState object and pass it to observers.


Let’s Code

Well, let’s imagine that we need to implement the News Station system where Radio and TV want to be noticed when news is released. To achieve it, our Radio and TV objects must be attached to Deuters News Station, then we’ll be able to set a new subject state in our Deuters(News Station Object), it automatically will call update to notify current observers.

To finalize it, when our observers (Radio and TV) receive a call from the notify method on Deuters, we’re able to consume that news.


First, let’s add the right folders and files to our project, and it should be like this.

. designpatterns
.. src
... Behavioral
.... Observer
..... NewsStation
...... Contracts
.. tests
... Behavioral
.... Observer
..... NewsStation

Let’s code

First, as usual, we’ll start creating our Contracts.

Observer.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Behavioral\Observer\NewsStation\Contracts;

interface Observer
{
    public function update(): void;
}

Subject.php

<?php

namespace DesignPatterns\Behavioral\Observer\NewsStation\Contracts;

abstract class Subject
{
    private array $observers = [];

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

    public function detach(Observer $removable) : void
    {
        $this->observers = array_filter($this->observers, function ($observer) use ($removable) {
            return $observer !== $removable;
        });
    }

    public function notify() : void
    {
        /** @var Observer $observer */
        foreach ($this->observers as $observer) {
            $observer->update();
        }
    }
}

Now we have two contracts, first one is just to ensure that our Observers will have the update method and the other one is our Subject that makes our ConcreteSubject or Observables notify other objects about his changes.

Now we can move to our concrete objects, in src/Behavioral/Observer/NewsStation folder

Deuters.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Behavioral\Observer\NewsStation;

use DesignPatterns\Behavioral\Observer\NewsStation\Contracts\Subject;

class Deuters extends Subject
{
    private string $subjectState = '';

    public function getSubjectState(): string
    {
        return $this->subjectState;
    }

    public function setSubjectState(string $subjectState): void
    {
        $this->subjectState = $subjectState;
        $this->notify();
    }
}

Radio.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Behavioral\Observer\NewsStation;

use DesignPatterns\Behavioral\Observer\NewsStation\Contracts\Observer;

class Radio implements Observer
{
    public string $observerState = '';

    public function __construct(public Deuters $deuters, public string $name)
    {
    }

    public function update(): void
    {
        $this->setObserverState($this->getName() . ' - ' . $this->getDeuters()->getSubjectState());
    }

    public function getObserverState(): string
    {
        return $this->observerState;
    }

    public function setObserverState(string $observerState): void
    {
        $this->observerState = $observerState;
    }

    public function getDeuters(): Deuters
    {
        return $this->deuters;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

TV.php

<?php

declare(strict_types=1);

namespace DesignPatterns\Behavioral\Observer\NewsStation;

use DesignPatterns\Behavioral\Observer\NewsStation\Contracts\Observer;

class TV implements Observer
{
    public string $observerState = '';

    public function __construct(public Deuters $deuters, public string $name)
    {
    }

    public function update(): void
    {
        $this->setObserverState($this->getName() . ' - ' . $this->getDeuters()->getSubjectState());
    }

    public function getObserverState(): string
    {
        return $this->observerState;
    }

    public function setObserverState(string $observerState): void
    {
        $this->observerState = $observerState;
    }

    public function getDeuters(): Deuters
    {
        return $this->deuters;
    }

    public function getName(): string
    {
        return $this->name;
    }
}

Before finishing this we need to create our test to be sure that everything is working as expected. In our src/Tests/Behavioral/Observer/NewsStation folder let’s create our file.

NewsStationTest.php

<?php

declare(strict_types=1);

namespace Tests\Behavioral\Observer\NewsStation;

use DesignPatterns\Behavioral\Observer\NewsStation\Deuters;
use DesignPatterns\Behavioral\Observer\NewsStation\Radio;
use DesignPatterns\Behavioral\Observer\NewsStation\TV;
use PHPUnit\Framework\TestCase;

class NewsStationTest extends TestCase
{
    public function testItCanNotifyObservers(): void
    {
        $subject = new Deuters();

        $radio   = new Radio($subject, 'Radio 081');
        $subject->attach($radio);

        $tv      = new TV($subject, 'TV News');
        $subject->attach($tv);

        $subject->setSubjectState('Here is the first news.');

        $this->assertEquals('Radio 081 - Here is the first news.', $radio->getObserverState());
        $this->assertEquals('TV News - Here is the first news.', $tv->getObserverState());

        $subject->detach($tv);

        $subject->setSubjectState('Here is another news.');
        $this->assertEquals('TV News - Here is the first news.', $tv->getObserverState());

        $this->assertEquals('Radio 081 - Here is another news.', $radio->getObserverState());
    }
}

Well, this test is self-explainable, but basically, we create our ConcreteSubject, here is the Deuters Object, then we also create our Radio and attach to subject we do exactly the same for our TV.

So we set our subject state in our Deuters and it will call notify(). Our first assertions are to verify if observers have updated their own state to right one.

After this we detach our TV and set a new subject state, it should update only the Radio object that remained as Deuters observers. The last assertions will verify if tv keeps with the first state and if Radio was updated.


You can follow our repository https://github.com/diegosm/designpatterns, everything is there.

I hope you understood this concept and feel free to ask something or do your contribution. There are many examples on the internet of this pattern.

This article is a part of our Design Pattern Series, follow our Design Patterns – What, when, why? link 🙂

1 Trackback or Pingback

Leave a Reply

Your email address will not be published. Required fields are marked *




Enter Captcha Here :