Design patterns (4 part series)
- Design patterns: Singleton
- Design patterns: Factory
- Design patterns: Observer
- Design patterns: Decorator
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 that 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.
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 objects observing it 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 the interface to keep the state consistent with that of the subject.
To achieve the observer pattern, there are four types of classes we need to implement:
Subject
List
of its Observer
objects.Attach
, Detach
, and Notify
methods.ConcreteSubject
Observer
Update
interface.ConcreteObserver
Update
interface.ConcreteSubject
.ConcreteSubject
.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. Subjectabstract 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 Subjectclass ConcreteSubject : Subject{public int SomeState { get; set; }}// 3. Observerabstract class Observer{public abstract void Update();}// 4. Concrete Observerclass 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);}}
There are all sorts of reasons why 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!");}}}
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.