DEV Community

Cover image for Design Patterns: Observer
Ryan
Ryan

Posted on

Design Patterns: Observer

Wassup! This is the third post that I'm writing while learning design patterns. My first two posts covered creational design patterns. This time we'll be taking a look at the Observer pattern which is a behavioral pattern. I'll first give a definition of the pattern, then show its implementation in C#, and finish with a code example.


Overview


Definition

Observer: a thing that watches or notices something. The Observer pattern allows us to create a one-to-many relationship between objects so that when one object changes, the many will be notified and update accordingly.

In this pattern, there is one Subject and many Observers. The Subject holds whatever state is of interest and notifies the Observers when the state changes. Each Observer keeps a reference to the Subject, stores the state that should be consistent with the Subject's, and implements the Observer updating interface to keep the state consistent with that of the Subject.


Implementation

To achieve the Observer pattern, there are 4 types of classes we need to implement:

  1. Subject
    • Stores List of its Observer objects.
    • Defines the Attach, Detach, and Notify methods.
    • Notifies the Observers when the state changes.
  2. Concrete Subject
    • Holds the state.
  3. Observer
    • Defines an Update interface.
  4. Concrete Observer
    • Implements the Update interface
    • Stores reference to the Concrete Subject
    • Stores state that should stay consistent with the Concrete Subject.

Using C#, the pattern looks like this:

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        ConcreteSubject subject = new ConcreteSubject();
        ConcreteObserver observer = new ConcreteObserver(subject);
        subject.Attach(observer);
        subject.SomeState = 100;
        subject.Notify();
    }
}

// 1. Subject
abstract class Subject
{
    List<Observer> observers = new List<Observer>();
    public void Attach(Observer observer)
    {
        observers.Add(observer);
    }

    public void Detach(Observer observer)
    {
        observers.Remove(observer);
    }

    public void Notify()
    {
        foreach (var observer in this.observers)
        {
            observer.Update();
        }
    }
}

// 2. Concrete Subject
class ConcreteSubject : Subject
{
    public int SomeState { get; set; }
}

// 3. Observer
abstract class Observer
{
    public abstract void Update();
}

// 4. Concrete Observer
class ConcreteObserver : Observer
{
    public int ObserverState { get; set; }

    ConcreteSubject MySubject { get; set; }

    public ConcreteObserver(ConcreteSubject subject)
    {
        this.MySubject = subject;
    }

    public override void Update()
    {
        this.ObserverState = MySubject.SomeState;
        Console.WriteLine("My state has been updated: " + this.ObserverState);
    }
}
Output:
My state has been updated: 100

You can play with this code here on .NET Fiddle


Example Code

There are all sorts of reasons we may want to use a subject/observer relationship while developing... this pattern is used a lot. Some examples include UI components reacting to one another, subscription services, app users following other users, etc.

Let's write a program that models sports fans cheering for their favorite player. The Subject will be the athlete and the Observers will be the fans. Every time the athlete scores, the fans will react to it in some way.

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        BasketballPlayer damianLillard = new BasketballPlayer("Damian Lillard");
        BasketballFan basketballFan1 = new BasketballFan(damianLillard);
        damianLillard.Attach(basketballFan1);
        damianLillard.Score(3);
        BasketballFan basketballFan2 = new BasketballFan(damianLillard);
        damianLillard.Attach(basketballFan2);
        damianLillard.Score(2);
    }

    abstract class Player
    {
        List<Fan> fans = new List<Fan>();

        public void Attach(Fan f)
        {
            fans.Add(f);
        }

        public void Detach(Fan f)
        {
            fans.Remove(f);
        }

        public void Notify()
        {
            foreach(Fan f in fans)
            {
                f.Update();
            }
        }
    }

    class BasketballPlayer : Player
    {
        public string Name;
        public int Points = 0;

        public BasketballPlayer(string name)
        {
            this.Name = name;
        }

        public void Score(int points)
        {
            this.Points += points;
            this.Notify();
        }
    }

    abstract class Fan
    {
        public abstract void Update();
    }

    class BasketballFan : Fan
    {
        BasketballPlayer FavoritePlayer;
        int FavoritePlayerPoints;

        public BasketballFan(BasketballPlayer player)
        {
            this.FavoritePlayer = player;
            this.FavoritePlayerPoints = this.FavoritePlayer.Points;
        }

        public override void Update()
        {
            this.FavoritePlayerPoints = this.FavoritePlayer.Points;
            Console.WriteLine(this.FavoritePlayer.Name + " has " + this.FavoritePlayerPoints + " points!");
        }
    }
}
Output:
Damian Lillard has 3 points!
Damian Lillard has 5 points!
Damian Lillard has 5 points!

You can play with this code here on .NET Fiddle


Disclaimer: There are a lot of resources for learning design patterns, and they can be implemented in different ways. I would recommend exploring more resources when finished with this post.

Discussion (0)