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

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 😂😂

Intent

Represents the view of the MVC pattern.

When an object changes its state, the observer pattern is to notify and update all dependencies of this object automatically.

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:

You have many objects that depend on one object.

You need to implement the MVC pattern in its implementation, you can use the observer pattern. The picture below illustrates this well.

Structure

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

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 😂😂

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:

C#

1

2

3

4

5

6

7

namespaceAuction

{

interfaceObserver

{

voidupdate();

}

}

And the bidder class looks like this:

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

namespaceAuction

{

classBidder:Observer

{

privatestringname;

publicdoublebidPrice;

publicBidder(stringname)

{

this.name=name;

}

publicvoidupdate()

{

Console.WriteLine(name+" propose: "+bidPrice+"\n");

if(bidPrice>450)

{

Console.WriteLine("Sold!! "+name);

}

}

publicvoidgiveNewPrice(doubleprice)

{

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.

C#

1

2

3

4

5

6

7

8

namespaceAuction

{

interfaceSubject

{

voidregisterBidder(Observero);

voidnotifyObservers();

}

}

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 🙂

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

namespaceAuction

{

classAuctioneer:Subject

{

privateList<Observer>observerList;

publicAuctioneer()

{

observerList=newList<Observer>();

}

publicvoiddisplayNewBidderPrice()

{

notifyObservers();

}

publicvoidregisterBidder(Observero)

{

observerList.Add(o);

}

publicvoidnotifyObservers()

{

foreach(ObserveroinobserverList)

{

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.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

namespaceAuction

{

classProgram

{

staticvoidMain(string[]args)

{

Auctioneer auctioner=newAuctioneer();

Bidder bidder1=newBidder("Slawek");

auctioner.registerBidder(bidder1);

Bidder bidder2=newBidder("Charlie");

auctioner.registerBidder(bidder2);

Bidder bidder3=newBidder("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:

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.

C#

1

2

3

4

5

6

7

8

namespaceObserverSchema

{

interfaceObserver

{

stringname{get;set;}

voidupdate(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.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

namespaceObserverSchema

{

classFacebookUser:Observer

{

publicstringname{get;set;}

stringpeople;

publicFacebookUser(stringname)

{

this.name=name;

}

publicvoidupdate(List<string>peoplecommenting)

{

peoplecommenting.Remove(name);

foreach(stringoinpeoplecommenting)

{

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.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

namespaceObserverSchema

{

classFacebookPost:Subject

{

privateList<Observer>observerList;

privateList<string>peoplecommenting;

publicFacebookPost()

{

observerList=newList<Observer>();

peoplecommenting=newList<string>();

}

publicvoidaddNewNotify()

{

Console.WriteLine("Adding new notify");

notifyObservers();

}

publicvoidcommentPost(Observero)

{

observerList.Add(o);

peoplecommenting.Add(o.name);

}

publicvoidnotifyObservers()

{

foreach(ObserveroinobserverList)

{

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.

C#

1

2

3

4

5

6

7

8

namespaceObserverSchema

{

interfaceSubject

{

voidcommentPost(Observero);

voidnotifyObservers();

}

}

And the customer.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

namespaceObserverSchema

{

classProgram

{

staticvoidMain(string[]args)

{

FacebookPost facebookPost=newFacebookPost();

FacebookUser facebookUser1=newFacebookUser("Slawek");

facebookPost.commentPost(facebookUser1);

FacebookUser facebookUser2=newFacebookUser("Charlie");

facebookPost.commentPost(facebookUser2);

facebookPost.addNewNotify();

FacebookUser facebookUser3=newFacebookUser("Nidhi");

facebookPost.commentPost(facebookUser3);

facebookPost.addNewNotify();

Console.ReadKey();

}

}

}

Result.

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.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

namespaceFacebookMulithreading

{

classFacebookPost:Subject

{

privateList<Observer>observerList;

privateList<string>peoplecommenting;

staticreadonlyobject_locker=newobject();

privatestaticFacebookPost instance=newFacebookPost();

publicstaticFacebookPost GetInstance()

{

returninstance;

}

publicFacebookPost()

{

observerList=newList<Observer>();

peoplecommenting=newList<string>();

}

publicvoidaddNewNotify()

{

lock(_locker)

{

Console.WriteLine("Adding new notify");

notifyObservers();

}

}

publicvoidcommentPost(Observero)

{

lock(_locker)

{

observerList.Add(o);

peoplecommenting.Add(o.name);

}

}

publicvoidnotifyObservers()

{

foreach(ObserveroinobserverList)

{

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.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

namespaceFacebookMulithreading

{

classProgram

{

staticvoidMain(string[]args)

{

FacebookPost facebookPost=FacebookPost.GetInstance();

Task task1=Task.Run(()=>

{

FacebookUser facebookUser1=newFacebookUser("Slawek");

facebookPost.commentPost(facebookUser1);

});

Task task2=Task.Run(()=>

{

FacebookUser facebookUser2=newFacebookUser("Charlie");

facebookPost.commentPost(facebookUser2);

facebookPost.addNewNotify();

});

Task task3=Task.Run(()=>

{

FacebookUser facebookUser3=newFacebookUser("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:

Or like this:

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

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

namespaceFacebookMulithreading

{

classProgram

{

staticvoidMain(string[]args)

{

FacebookPost facebookPost=FacebookPost.GetInstance();

Task task1=Task.Run(()=>

{

FacebookUser facebookUser1=newFacebookUser("Slawek");

facebookPost.commentPost(facebookUser1);

});

task1.Wait();

Task task2=Task.Run(()=>

{

FacebookUser facebookUser2=newFacebookUser("Charlie");

facebookPost.commentPost(facebookUser2);

facebookPost.addNewNotify();

});

task2.Wait();

Task task3=Task.Run(()=>

{

FacebookUser facebookUser3=newFacebookUser("Nidhi");

facebookPost.commentPost(facebookUser3);

facebookPost.addNewNotify();

});

task3.Wait();

Console.ReadKey();

}

}

}

And thanks to this we have such a result again 🙂

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:

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.

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

namespaceAuctionEvents

{

publicdelegatevoidNotifyObserver();

publicclassAuctioneer:Subject

{

publiceventNotifyObserver NotifyObserverEvent;

publicvoidregisterBidder(NotifyObserver ob)

{

NotifyObserverEvent+=ob;

}

publicvoiddisplayNewBidderPrice()

{

notifyObservers();

}

publicvoidnotifyObservers()

{

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.

C#

1

2

3

4

5

6

7

8

namespaceAuctionEvents

{

interfaceSubject

{

voidregisterBidder(NotifyObservero);

voidnotifyObservers();

}

}

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 🙂

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

namespaceAuctionEvents

{

classProgram

{

staticvoidMain(string[]args)

{

Auctioneer auctioner=newAuctioneer();

Bidder bidder1=newBidder("Slawek");

auctioner.registerBidder(bidder1.update);

Bidder bidder2=newBidder("Charlie");

auctioner.registerBidder(bidder2.update);

Bidder bidder3=newBidder("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 🙂

Relations with other design patterns

The mediator can use the observer pattern to dynamically register colleagues and communicate with them.

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.

Mediator and Observer are competing patterns. The difference between them is that Observer distributes communication by introducing “observer” and “subject” objects, whereas a Mediator object encapsulates the communication between other objects.

Mediator patterns, chain of responsibility, Command, Observer show how you can separate recipients and senders. The chain of responsibility passes the request along the chain of potential recipients. Command normally specifies a sender-receiver connection with a subclass. Mediator has senders and receivers reference each other indirectly. Observer defines a very decoupled interface that allows for multiple receivers to be configured at run-time.