Design patterns: Observer

in #design-patterns โ€ข 7 years ago

Hello again!! We are taking care of another design pattern, which in my opinion must be known compulsorily ๐Ÿ™‚. And it is an observer pattern.



ObserverDesignPattern.jpg

Discussion

The main purpose of this pattern is for example when the state of an object changes, so that all the dependencies of this object are notified and updated. In a nutshell, the observer pattern works in a way that defines the Subject, which can be said is the data model, in this is the main logic that updates the states of objects, passing objects of the Observer in turn. The observer pattern works well with the MVC pattern, representing the view in this pattern.

Observer pattern is used in facebook when, for example, you get a notification about an event or when you receive a notification, if someone else commented on a post or, for example, youtube when someone subscribes to a channel.

The principle of the observer pattern is also described in the picture below ๐Ÿ˜‚๐Ÿ˜‚

javascript-common-design-patterns-76-638.jpg

Intent

  1. Represents the view of the MVC pattern.
  2. When an object changes its state, the observer pattern is to notify and update all dependencies of this object automatically.
  3. Separates the main logic of the pattern or the Subject from the Observer objects variables.

Problem

You have a monitoring mechanism in some project, which is not scalable, eg it requires pulling any dependencies or there are still some problems or requirements deal with monitoring.

Letโ€™s take an example from life letโ€™s assume that the customer goes to the store to see if the product is available, most of these trips will be nonsense, because in most cases this product will not be available. It would be best if the seller had a system that he could notify the customer about the availability of a given product, e.g. by email.

Use when:

  1. You have many objects that depend on one object.
  2. You need to implement the MVC pattern in its implementation, you can use the observer pattern. The picture below illustrates this well.

design-patterns-tutorials-60-728.jpg

Structure

And this is how the structure of the observer pattern looks like.

Observer.png

As you can see, the ViewOne and ViewTwo classes inherit from the Observer interface, so that all of them have the update() method, in the Subject we usually have a list in which all objects of the Observer are saved. We update all of these objects by calling the update() method in the foreach loop that iterates through the collections everything will be visible right on the specific example. ๐Ÿ™‚

Example

Auction

Letโ€™s do the auction example first, the auctioneer approves the ever higher prices of a product there and sends a message to other objects in our example bidding a new product price.

Easy ๐Ÿ™‚ observer is a fairly simple design pattern, so the reaction in practical examples should not be like that of the this Lord below ๐Ÿ˜‚๐Ÿ˜‚

design-pattern-automation-41-638.jpg

Letโ€™s see the code of objects observers in our case, letโ€™s start from the Observer interface, which is the basic element of the class in the observer pattern, it looks like this:

namespace Auction
{
    interface Observer
    {
        void update();
    }
}

And the bidder class looks like this:

namespace Auction
{
    class Bidder : Observer
    {
        private string name;
        public double bidPrice;

        public Bidder(string name)
        {
            this.name = name;
        }

        public void update()
        {
            Console.WriteLine(name + " propose: " + bidPrice+"\n");

            if (bidPrice > 450)
            {
                Console.WriteLine("Sold!! " + name);
            }
        }

        public void giveNewPrice(double price)
        {
            bidPrice = price;
        }
    }
}

In this class, we extend the update() method and if the price exceeds 450 then we end the auction.

We will now see the class of the auctioneer, who inherits from the Subject interface, which interface is used to implement methods for registering bidders and notifying them about the new price.

First the Subject interface.

namespace Auction
{
    interface Subject
    {
        void registerBidder(Observer o);
        void notifyObservers();
    }
}

And the auctioneer class in our case Auctioneer class. It can be said that the Auctioneer class and the
interface of the Subject is our entire main logic, i.e. Subject of our pattern observer ๐Ÿ™‚

namespace Auction
{
    class Auctioneer:Subject
    {
        private List<Observer> observerList;

        public Auctioneer()
        {
            observerList = new List<Observer>();
        }

        public void displayNewBidderPrice()
        {
            notifyObservers();
        }

        public void registerBidder(Observer o)
        {
            observerList.Add(o);
        }

        public void notifyObservers()
        {
            foreach (Observer o in observerList)
            {
                o.update();
            }
        }
    }
}

As you can see, the observers are saved in the observerList list. And we notify users about the new price by calling the notifyObservers() method which has a foreach loop that iterates through observerList list.

And finally the customer.

namespace Auction
{
    class Program
    {
        static void Main(string[] args)
        {
            Auctioneer auctioner = new Auctioneer();

            Bidder bidder1 = new Bidder("Slawek");
            auctioner.registerBidder(bidder1);

            Bidder bidder2 = new Bidder("Charlie");
            auctioner.registerBidder(bidder2);

            Bidder bidder3 = new Bidder("Nidhi");
            auctioner.registerBidder(bidder3);

            bidder1.giveNewPrice(123);
            bidder2.giveNewPrice(158);
            bidder3.giveNewPrice(208);

            auctioner.displayNewBidderPrice();

            bidder1.giveNewPrice(243);
            bidder2.giveNewPrice(358);
            bidder3.giveNewPrice(458);

            auctioner.displayNewBidderPrice();

            Console.ReadKey();
        }
    }
}

You can see that we create observer objects, conquer the price and notify observers about new price.

Result:

observerfirst.png

Real-life example

Notifications about new comments

We will now make an example of a mechanism notifying about new comments under some posts, just like it works on facebook,

Letโ€™s start as in the previous example from the Observer interface and the observer class, in our case, people who commenting the post.

namespace ObserverSchema
{
    interface Observer
    {
        string name { get; set; }
        void update(List<string> peoplecommenting);
    }
}

We can see that the interface looks a bit different now, we will have to send a list of users to notify everyone who has commented on the post and the username at which we are currently being iterated. However, the general principle of the oberwatora does not change, we give only a bit more data.

And the observer class in our case, the commenting person.

namespace ObserverSchema
{
    class FacebookUser : Observer
    {
        public string name { get;set; }
        string people;

        public FacebookUser(string name)
        {
            this.name = name;
        }

        public void update(List<string> peoplecommenting)
        {
            peoplecommenting.Remove(name);

            foreach (string o in peoplecommenting)
            {
                people += o+" ";
            }

            if(peoplecommenting.Count>0)
            {
                Console.WriteLine("Hello " + name + "!");
                Console.WriteLine(people + "also commented the post\n");
            }
            
        }
    }
}

This class in short works so that if people commenting on the post is more than one, then we display their names in turn, of course, at the end of the function we remove the name of the man from which is currently iterating, the person who comments simply knows that he is commenting given post ๐Ÿ™‚

Letโ€™s now look at the class that inherits the interface, which, as in the previous example, is used to implement methods of notifying and registering users in our case it is called FacebookPost.

namespace ObserverSchema
{
    class FacebookPost : Subject
    {
        private List<Observer> observerList;
        private List<string> peoplecommenting;

        public FacebookPost()
        {
            observerList = new List<Observer>();
            peoplecommenting = new List<string>();
        }

        public void addNewNotify()
        {
            Console.WriteLine("Adding new notify");
            notifyObservers();
        }

        public void commentPost(Observer o)
        {
            observerList.Add(o);
            peoplecommenting.Add(o.name);
            
        }

        public void notifyObservers()
        {
            foreach (Observer o in observerList)
            {
                o.update(peoplecommenting);
            }
        }
    }
}

Much does not differ from the equivalent of this class in the previous Auctioneer example, we only add another list that saves the names of the commenters.

Also the Subject interface without major changes.

namespace ObserverSchema
{
    interface Subject
    {
        void commentPost(Observer o);
        void notifyObservers();
    }
}

And the customer.

namespace ObserverSchema
{
    class Program
    {
        static void Main(string[] args)
        {
            FacebookPost facebookPost = new FacebookPost();

            FacebookUser facebookUser1 = new FacebookUser("Slawek");
            facebookPost.commentPost(facebookUser1);

            FacebookUser facebookUser2 = new FacebookUser("Charlie");
            facebookPost.commentPost(facebookUser2);
            facebookPost.addNewNotify();

            FacebookUser facebookUser3 = new FacebookUser("Nidhi");
            facebookPost.commentPost(facebookUser3);
            facebookPost.addNewNotify();

            Console.ReadKey();
        }
    }
}

Result.
observersecond.png

If you wanted to secure it in a multi-threaded way, the FacebookPost class and the client we would have to change.

Letโ€™s start with the FacebookPost class.

namespace FacebookMulithreading
{
    class FacebookPost : Subject
    {
        private List<Observer> observerList;
        private List<string> peoplecommenting;
        static readonly object _locker = new object();
        private static FacebookPost instance = new FacebookPost();

        public static FacebookPost GetInstance()
        {
            return instance;
        }

        public FacebookPost()
        {
            observerList = new List<Observer>();
            peoplecommenting = new List<string>();
        }

        public void addNewNotify()
        {
            lock (_locker)
            {
                Console.WriteLine("Adding new notify");
                notifyObservers();
            }
        }

        public void commentPost(Observer o)
        {
            lock (_locker)
            {
                observerList.Add(o);
                peoplecommenting.Add(o.name);
            }
        }

        public void notifyObservers()
        {
            foreach (Observer o in observerList)
            {
                o.update(peoplecommenting);
            }
        }
    }
}

I added the GetInstance method so that there could be only one FacebookPost class object, we know the singleton.๐Ÿ™‚ I have also added the word lock, which has also been rolled many times, in the article about the concurrency is the explanation of the word lock. But for those who do not know this word, I will say in short that this word secures access from two threads at the same moment. Of course, everyone must match the solution to their language in Java or C ++, it is known that in these languages it will look different.

Letโ€™s see the customer yet.

namespace FacebookMulithreading
{
    class Program
    {
        static void Main(string[] args)
        {
            FacebookPost facebookPost=FacebookPost.GetInstance();

            Task task1 = Task.Run(() =>
            {
                FacebookUser facebookUser1 = new FacebookUser("Slawek");
                facebookPost.commentPost(facebookUser1);
            });

            Task task2 = Task.Run(() =>
            {
                FacebookUser facebookUser2 = new FacebookUser("Charlie");
                facebookPost.commentPost(facebookUser2);
                facebookPost.addNewNotify();
            });

            Task task3 = Task.Run(() =>
            {
                FacebookUser facebookUser3 = new FacebookUser("Nidhi");
                facebookPost.commentPost(facebookUser3);
                facebookPost.addNewNotify();
            });

            Console.ReadKey();
        }
    }
}

It is not too difficult. We simply create threads using tasks. However, due to the nature of asynchonity, everything is done at the same moment, no error will fail because we have already made the appropriate changes, only the result may be otherwise displayed, eg:

observerfistmultithreading.png

Or like this:

Observersecondmultithreading.png

If you want it to display normally, you have to executing threads one by one, we have to make such changes in the client.

namespace FacebookMulithreading
{
    class Program
    {
        static void Main(string[] args)
        {
            FacebookPost facebookPost=FacebookPost.GetInstance();

            Task task1 = Task.Run(() =>
            {
                FacebookUser facebookUser1 = new FacebookUser("Slawek");
                facebookPost.commentPost(facebookUser1);
            });

            task1.Wait();

            Task task2 = Task.Run(() =>
            {
                FacebookUser facebookUser2 = new FacebookUser("Charlie");
                facebookPost.commentPost(facebookUser2);
                facebookPost.addNewNotify();
            });

            task2.Wait();

            Task task3 = Task.Run(() =>
            {
                FacebookUser facebookUser3 = new FacebookUser("Nidhi");
                facebookPost.commentPost(facebookUser3);
                facebookPost.addNewNotify();
            });

            task3.Wait();

            Console.ReadKey();
        }
    }
}

And thanks to this we have such a result again ๐Ÿ™‚

observersecond.png


Time for a lame joke ๐Ÿ™‚

Whatโ€™s the difference between an oral thermometer and a rectal thermometer?
The taste.

Okay, those jokes are extremely not funny ๐Ÿ˜, weโ€™ll go further ๐Ÿ™‚


###Events and delegates in the observer pattern

The above examples that we did can also be done using events and delegates in C #. The only difference is that the code is more understandable, readable and elegant. The simplest example below:

namespace EventsObserver
{
    class Program
    {
        static void Main(string[] args)
        {
            Product myProduct = new Product();
            myProduct.OnStateChange += newState => Console.WriteLine("State changed to "+ newState);
            myProduct.MyState = State.State3;

            Console.ReadKey();
        }
    }

    delegate void StateChangeHandler(State newState);

    enum State
    {
        State1, State2, State3
    }

    class Product
    {
        private State _state;

        public State MyState
        {
            get { return _state; }
            set
            {
                _state = value;
                _onChange(_state);
            }
        }

        private event StateChangeHandler _onChange;

        public event StateChangeHandler OnStateChange
        {
            add
            {
                _onChange += value;
            }
            remove
            {
                _onChange -= value;
            }
        }
    }
}

Result:

eventsobserver.png

Letโ€™s do an example with an auction now, this is the first example in this article only we will now make this example using delegates and events. We have a lot to change in the Auctioneer class and we only need to change a bit in the client and the Subject interface.

First, the Auctioneer class.

namespace AuctionEvents
{
    public delegate void NotifyObserver();

    public class Auctioneer: Subject
    {
        public event NotifyObserver NotifyObserverEvent;
        
        public void registerBidder(NotifyObserver ob)
        {
            NotifyObserverEvent += ob;
        }

        public void displayNewBidderPrice()
        {
            notifyObservers();
        }

        public void notifyObservers()
        {
            NotifyObserverEvent();
        }
    }
}

You can see that the code is much less and is much more readable. And so it is supposed to be ๐Ÿ™‚

In the Subject interface, we only change the type of argument used in the registerBidder() method on the NotifyObserver.

namespace AuctionEvents
{
    interface Subject
    {
        void registerBidder(NotifyObserver o);
        void notifyObservers();
    }
}

In the client in the call the registerBidder() method, we only need to pass the name of the method, i.e. update. We know how delegates and events work ๐Ÿ™‚ And if not then look here ๐Ÿ™‚

namespace AuctionEvents
{
    class Program
    {
        static void Main(string[] args)
        {
            Auctioneer auctioner = new Auctioneer();

            Bidder bidder1 = new Bidder("Slawek");
            auctioner.registerBidder(bidder1.update);

            Bidder bidder2 = new Bidder("Charlie");
            auctioner.registerBidder(bidder2.update);

            Bidder bidder3 = new Bidder("Nidhi");
            auctioner.registerBidder(bidder3.update);

            bidder1.giveNewPrice(123);
            bidder2.giveNewPrice(158);
            bidder3.giveNewPrice(208);

            auctioner.displayNewBidderPrice();

            bidder1.giveNewPrice(243);
            bidder2.giveNewPrice(358);
            bidder3.giveNewPrice(458);

            auctioner.displayNewBidderPrice();

            Console.ReadKey();
        }
    }
}

As you can see, we do not have to pass the entire Bidder class object, we can say that in .NET it was done for us thatโ€™s why I like this platform because it shortens many things ๐Ÿ™‚

The result, of course, the same ๐Ÿ™‚

observerfirst.png

Relations with other design patterns

  1. The mediator can use the observer pattern to dynamically register colleagues and communicate with them.
  2. The difference between the mediator and the observer is that the observer uses the Subject and Observer objects for communication and the mediator encapsulates the objects that communicate with each other.

Summary

Thatโ€™s all about Observer๐Ÿ™‚.

Link to github with the whole code from this article: https://github.com/Slaw145/Observer

This content also you can find on my blog: http://devman.pl/programtech/design-patterns-observer/

And on medium: https://medium.com/@sawomirkowalski/design-patterns-observer-5832ad7e0ddf

If you recognise it as useful, share it with others so that others can also use it.

Leave upvote and follow and wait for next articles :) .

In the next article, we will talk about the Visitor pattern.

As a standard, I remind you about the newsletter, which I send notifications about new entries and additional information about the IT world in general.๐Ÿ™‚

And NECESSERILY join the DevmanCommunity community on fb, part of the community is in one place ๐Ÿ™‚

โ€“ site on fb: Devman.pl-Slawomir Kowalski

โ€“ group on fb: DevmanCommunity

Ask, comment underneath at the end of the post, share it, rate it, whatever you want๐Ÿ™‚.

Illustrations, pictures and diagrams are from: https://sourcemaking.com/design_patterns/observer