Star Wars Meets Software Development: A Guide to Implementing the Observer Pattern in C#
Luke: Master Yoda, I need guidance
Yoda: Yes, young Luke
Luke: "Master Yoda, I am having trouble communicating with the other Jedi in the council. We are all working on different missions, and I do not know what is happening with them."
Yoda: "Ah, young Padawan, you are experiencing a problem with coordination and communication. In programming, this is a common problem that can be addressed with the Observer pattern.
Yoda: For example, we can use this pattern to keep track of the status of Jedi missions. Let's say that you have a JediCouncil class, which contains a list of Jedi Knights who are undertaking different missions. Each of these Jedi Knights has a MissionStatus property that indicates whether the mission is ongoing, completed, or failed.
Yoda: In C#, we can implement the Observer pattern by having the JediCouncil class implement the IObservable interface, and each JediKnight class implement the IObserver interface.
public class JediCouncil : IObservable<JediKnight>
{
private List<JediKnight> _jediKnights;
private List<IObserver<JediKnight>> _observers;
public JediCouncil()
{
_jediKnights = new List<JediKnight>();
_observers = new List<IObserver<JediKnight>>();
}
public void AddJediKnight(JediKnight jediKnight)
{
_jediKnights.Add(jediKnight);
NotifyObservers(jediKnight);
}
public void RemoveJediKnight(JediKnight jediKnight)
{
_jediKnights.Remove(jediKnight);
}
public IDisposable Subscribe(IObserver<JediKnight> observer)
{
_observers.Add(observer);
return new Unsubscriber(_observers, observer);
}
private void NotifyObservers(JediKnight jediKnight)
{
foreach (var observer in _observers)
{
observer.OnNext(jediKnight);
}
}
private class Unsubscriber : IDisposable
{
private List<IObserver<JediKnight>> _observers;
private IObserver<JediKnight> _observer;
public Unsubscriber(List<IObserver<JediKnight>> observers, IObserver<JediKnight> observer)
{
_observers = observers;
_observer = observer;
}
public void Dispose()
{
if (_observer != null && _observers.Contains(_observer))
{
_observers.Remove(_observer);
}
}
}
}
using System;
using System.Collections.Generic;
public class Program
{
public static void Main(string[] args)
{
var jediCouncil = new JediCouncil();
var Luke = new JediKnight {Name = "Luke Skywalker", Status = MissionStatus.Ongoing};
var Leia = new JediKnight {Name = "Leia Organa", Status = MissionStatus.Ongoing};
var Han = new JediKnight {Name = "Han Solo", Status = MissionStatus.Ongoing};
jediCouncil.AddJediKnight(Luke);
jediCouncil.AddJediKnight(Leia);
jediCouncil.AddJediKnight(Han);
var observer1 = jediCouncil.Subscribe(new JediCouncilObserver());
//Change the mission status of Luke, triggers the OnNext on the observer
Luke.Status = MissionStatus.Completed;
//Change the mission status of Leia, triggers the OnNext on the observer
Leia.Status = MissionStatus.Failed;
//Unsubscribe observer1
observer1.Dispose();
//Change the mission status of Han, it will not trigger OnNext, observer1 is unsubscribed
Han.Status = MissionStatus.Completed;
}
}
public class JediKnight : IObserver<JediKnight>
{
public string Name { get; set; }
public MissionStatus Status { get; set; }
public void OnCompleted()
{
// Notify the observer that all updates have been received
}
public void OnError(Exception error)
{
// Notify the observer of an error
}
public void OnNext(JediKnight jediKnight)
{
// Update the status of the Jedi Knight
}
}
Other design pattern articles:
Decorator Pattern: https://zahere.com/yoda-and-the-decorator-pattern-a-lesson-for-luke
Strategy and Factory Pattern: https://zahere.com/opinion-strategy-pattern-factory-pattern-will-be-the-most-used-design-pattern-in-your-career
If you liked my content, do kindly like and share in your network. And don't forget to subscribe to the newsletter to NEVER miss an article.