Observer - Design Pattern

Observer - Design Pattern

Software Architecture Simplified

Objective 🎯

Define a one-to-many dependency between objects in a way that when one object changes its state or any specific attribute, all its dependents (a.k.a. observers) will be notified automatically.

Type

✔️Behavioral: Describes how objects interact/communicate between themselves.

Creational: Describes how to instantiate an object without large and complex.

Structural: Describes how objects/classes are composed to form larger structures.

UML 📐

image.png

Participants 🔗

• Subject:

  • Holds a list of observers
  • Provides an interface for attaching/detaching Observer objects

• ConcreteSubject:

  • Sends a notification to its observers when its state/properties change

• Observer:

  • Defines an interface to allow objects to be notified when the subject changes

• ConcreteObserver:

  • Maintains a reference to a ConcreteSubject object
  • Stores an internal state that should be consistent with the Subject’s state (if needed)
  • Implements the Observer updating interface to keep its state in sync with the Subject’s

Sample Code 🎮

Structural Example 🏛️

image.png

public static class ObserverStructural
    {
        public static void Execute()
        {
            ConcreteSubject lConcreteSubject = new ConcreteSubject();
            lConcreteSubject.Attach(new ConcreteObserver1(lConcreteSubject));
            lConcreteSubject.Attach(new ConcreteObserver2(lConcreteSubject));

            lConcreteSubject.SubjectState = "State2";
            lConcreteSubject.SubjectState = "State3";
        }
    }

    public abstract class Subject
    {
        private List<Observer> _Observers = new List<Observer>();

        public void Attach(Observer prObserver)
        {
            _Observers.Add(prObserver);
        }

        public void Detach(Observer prObserver)
        {
            _Observers.Remove(prObserver);
        }

        protected void NotifyObservers()
        {
            foreach (Observer lObserverCurrent in _Observers)
            {
                lObserverCurrent.Update();
            }
        }
    }

    public abstract class Observer
    {
        public ConcreteSubject _ConcreteSubject;

        public string _Name { get; set; }
        public string _ObserverState { get; set; }

        public Observer(ConcreteSubject prConcreteSubject)
        {
            _ConcreteSubject = prConcreteSubject;
        }

        public abstract void Update();
    }

    public class ConcreteObserver1 : Observer
    {
        public ConcreteObserver1(ConcreteSubject prConcreteSubject) : base(prConcreteSubject) { }

        public override void Update()
        {
            Console.WriteLine("Concrete Observer 1 Triggered");
        }
    }

    public class ConcreteObserver2 : Observer
    {
        public ConcreteObserver2(ConcreteSubject prConcreteSubject) : base(prConcreteSubject) { }

        public override void Update()
        {
            Console.WriteLine("Concrete Observer 2 Triggered");
        }
    }

    public class ConcreteSubject : Subject
    {
        private string _SubjectState = "State1";
        public string SubjectState
        {
            get { return _SubjectState; }
            set
            {
                Console.WriteLine($"Changing Subject's State from {_SubjectState} to {value}");
                _SubjectState = value;
                NotifyObservers();
            }
        }
    }

Output

image.png

Real-world Example 🔥

image.png

public static class ObserverPractical
    {
        public static void Execute()
        {
            JohnPoliticalFigure lJohnPoliticalFigure = new JohnPoliticalFigure();
            lJohnPoliticalFigure.Attach(new Mike());
            lJohnPoliticalFigure.Attach(new Harry());

            lJohnPoliticalFigure.NewTweet("Lorem ipsum dolor sit amet, consectetur adipiscing");
            lJohnPoliticalFigure.NewTweet("Mauris vitae orci a dolor bibendum gravida");
        }
    }

    public abstract class TwitterProfile
    {
        private List<TwitterFollower> _Followers = new List<TwitterFollower>();

        public void Attach(TwitterFollower prFollower)
        {
            _Followers.Add(prFollower);
        }

        public void Detach(TwitterFollower prFollower)
        {
            _Followers.Remove(prFollower);
        }

        protected void NotifyFollowers(string prTweet)
        {
            foreach (TwitterFollower lFollowerCurrent in _Followers)
            {
                lFollowerCurrent.NotifyNewTweet(prTweet);
            }
        }
    }

    public abstract class TwitterFollower
    {
        public abstract void NotifyNewTweet(string prTweet);
    }

    public class Mike : TwitterFollower
    {
        public override void NotifyNewTweet(string prTweet)
        {
            Console.WriteLine($"Notifying User \"Mike\"");
        }
    }

    public class Harry : TwitterFollower
    {
        public override void NotifyNewTweet(string prTweet)
        {
            Console.WriteLine($"Notifying User \"Harry\"");
        }
    }

    public class JohnPoliticalFigure : TwitterProfile
    {
        public void NewTweet(string prTweet)
        {
            Console.WriteLine("New Tweet from John: " + prTweet);
            NotifyFollowers(prTweet);
        }
    }

Output

image.png

Source Code 🎲

github.com/VictorLins/DesignPatterns

Did you find this article valuable?

Support Victor Lins by becoming a sponsor. Any amount is appreciated!