Observer Pattern Tutorial: I NEVER knew events were THIS powerful 🚀

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments

Finally a tutorial that teaches logic behind it not just do this do that and voila!

👍︎︎ 91 👤︎︎ u/tutami 📅︎︎ Feb 19 2021 🗫︎ replies

The fact that you've explicitly created a subscription model, makes this the Publisher-Subscriber pattern.

With the Observer pattern, observers must be aware of the existence of the observee, but that's not the case here. Instead, your functions have no knowledge of the handlers nor the handlers of the functions, they only know of a string being passed (published) to a central location.

Here are a couple SO posts about event systems in Python:

  1. Difference between Observer, Pub/Sub, and Data Binding

  2. Event system in Python

Note: an argument can be made that Pub-Sub is a variant of Observer, but the argument isn't a strong one

👍︎︎ 75 👤︎︎ u/mahtats 📅︎︎ Feb 19 2021 🗫︎ replies

I don't completely agree this change improved the code a lot. Yes, the number of imports was reduced, but so now is reading the code more difficult—what happens at this point of code? How does it help in the overall task? Ultimately actually registering multiple event handlers in this kind of scenario would be only useful for debugging; admittedly it was nice how disabling adding the handlers disabled the function altogether.

There is still coupling: to understand what kind of parameters an event handler accepts, you need to find the site where the handler is defined, and the best way to do that is with grep, whereas with the original code the import directly tells you where the code is coming from. (I also enjoy using mypy so I guess I'm not a typical python developer; which, indeed, I don't write a lot of.)

I find event handling best suited for code where the hooked function doesn't really expect or care anything in particular to happen for it to complete its job. So, for example if one part of the code would need to do setups when a new client connects or cleanups when the client disconnects; that's where I would use events.

👍︎︎ 32 👤︎︎ u/eras 📅︎︎ Feb 19 2021 🗫︎ replies

...not to be confused with threading.Event or asyncio.Event, which are also pretty powerful for synchronizing concurrent code.

👍︎︎ 18 👤︎︎ u/LightShadow 📅︎︎ Feb 19 2021 🗫︎ replies

Python has a defaultdict. It would make the event.py file a lot easier:

from collections import defaultdict

subscribers = defaultdict(list)

def subscribe(event_type: str, fn):
    subscribers[event_type].append(fn)

def post_event(event_type: str, data):
    for fn in subscribers[event_type]:
        fn(data)

While the observer pattern is very powerful, it must be mentioned that it also comes with a few drawbacks.

One drawback is that it is difficult to see what happens when an event is triggered. For instance: your program crashes when a new user is registered. How will you figure out which event listener caused the bug?

An other drawback is that the decoupling adds extra parts that can misbehave. For instance: No welcome email when a new user is registered. What is the cause? The new-user-function, the event-listener, the send-email-module, or was email not subscribed to register?

👍︎︎ 7 👤︎︎ u/TheMsDosNerd 📅︎︎ Feb 19 2021 🗫︎ replies

This was actually brilliant! I didn't really understand why you're doing it until you gave the example (switching from slack to teams) in the end. Subscribed! May i suggest, if you use an example in the beginning of why/how a technique would help that would make it even better. Either way, really enjoyed it!

👍︎︎ 6 👤︎︎ u/Brown_Mamba_07 📅︎︎ Feb 19 2021 🗫︎ replies

This could probably be applied by a lot of people writing discord bots with discord.py

👍︎︎ 14 👤︎︎ u/ivosaurus 📅︎︎ Feb 19 2021 🗫︎ replies

I learned a lot - thank you! Subscribed and liked.

👍︎︎ 7 👤︎︎ u/theInfinite_Mind 📅︎︎ Feb 19 2021 🗫︎ replies

Events are needlessly abstract and only make your code more difficult to understand /troubleshoot in the future. Just use threads and listeners.

CMV

👍︎︎ 10 👤︎︎ u/MrDysprosium 📅︎︎ Feb 19 2021 🗫︎ replies
Captions
in this video i'm going to show you how to use  the observer pattern to create better separation   between various modules in your code this pattern  allows you to do some really cool things i'm going   to show you an example the observer pattern  also called the listener sometimes is actually   a classic from the gang of four book on design  patterns here's a class diagram the important   thing to note is that there are two roles the  subject and the observer the subject does things   and changes things and notifies observers of any  changes that happened there's several variations   of this pattern in this video i'm actually  not going to implement exactly the classical   object-oriented version of it but you commonly  see it in event management systems and this is   also the way that most developers use this pattern  handle user interface or general system events   this is a pity because the pattern can be very  powerful and work for you and i'm going to show   you how to use that to your advantage in a real  world example suppose you write a back-end api   and you work on a register user function next to  the database operations this function also sends   a message to slack to the sales team it sends  a welcome email to the user and it writes a log   to a server somewhere now it's possible that you  write a function that looks something like this that's not good let's write something better   let's first go over the structure of this example  the example is a little bit more involved than the   previous ones because i wanted to show something  that kind of approaches the situation like you   would encounter in real life the example that  i have here imports a few functions from an api   folder and it does a few things it registers  a user it sends a password reset message and   upgrades the plan of the user now normally these  things would be api calls i just put them right   here in the code just for testing purposes if you  look at the api folder we have a user file and a   plan found these actually contain the methods that  i was calling just there so in user for example   we have to register new user which takes a name  password and email address and we have a password   forgotten function that takes an email address so  if you look at the body there's one part which is   the actual job that it needs to do like creating a  user and then it posts a few messages like here it   posts a slack message so the sales department  can spam the person it sends a welcome mail   it writes something to the server log etc etc and  these things are all imported from a lib folder   at the moment this doesn't really do anything  like for example this send email message doesn't   actually send an email but it simply prints the  message to the console similar for log and for   slack in a real application obviously this would  be integrated with the actual services that you'd   be using the database is the most complicated of  all these things in the sense that it's also not a   real database but it creates an array of users it  has a class user that has name password email and   a few other things and then it has a few functions  so you can create a user and you can find the user   so these are things that you'd normally have  in a database driver but here in this example   obviously we're just doing that locally now the  problem with this code is that if you look at for   example register new user you see it's doing a lot  of different things it's overall a method that has   pretty weak cohesion not only does it need to know  about database stuff it also needs to know about   slack email logs etc now because register new user  needs to know about all these particular functions   it means you also have a lot of different imports  here and similar for password forgotten so it also   does the actual job it needs to do which is find  the user and generate a password reset code and   then it sends a message it writes something to the  server etc etc and for the part of the application   dealing with upgrading the user's plan it actually  looks pretty similar part of this job is doing the   changes in the database which is finding the user  and then upgrading the plan in the database but   then it also posts a slack message sends an email  writes something to the log and you see also here   there's a lot of different imports that means that  upgrade plan just like register new user also has   pretty weak cohesion and is directly coupled  with all these very particular implementations   so that sounds very nice and we want to change  that and this is where the observer pattern or   specifically an event system can really help you  separate these things so let's try to rewrite this   application so that it uses events instead of  these direct couplings between the code and the   messaging python has several event libraries  but just to show how it works i'm going to   write my own it's it's actually pretty simple  so let's add an event file here event dot pi   and basically the event system is very simple  you have a number of subscribers that subscribe   to different types of events and whenever you  post an event you need to notify the subscribers   so let's create first a dictionary containing  the subscribers now we have a dictionary and   then each item in the dictionary is going  to be for that particular type of event   the list of subscribers that need to  be notified when that event happens so let's add a subscribe  method that does that for us   so we're giving it an event type which is  basically just a string and a function that   needs to be called i'm not going to use  type hints here for the function because   frankly in python type hints for functions are  a complete mess the only thing we have to do   when we subscribe to a particular event type  is first check that there is already a list   of that event type in the dictionary if not we  need to create one and then add it to the list so if not already in the  subscriber list then let's create   a list of subscribers for this particular event and we're directly appending the  function that needs to be called   to the subscriber list now again there are already  event library so probably you won't have to write   this yourself i'm just adding it so it shows  you how the pattern actually works and then next   to subscribing we also need to be able to post  events so let's add a function for that as well and normally when you post an event you  generally want to pass along some data with it so if there are no subscribers if the  event type doesn't exist in dictionary   subscribers then we don't have to do anything otherwise we're going to  go over the entire list of   subscriber functions and call each  of those functions with the data so there you have it this is actually a complete  well almost complete event system and this   actually works just fine so now what we can do  we can go back to for example the user functions   like registering new user and then replace all  this stuff with simply a posting an event goal and as the data i'm going to pass  along the user and same thing here then of course i also need to up update my imports  and now i can remove parts of the imports that   are no longer needed here and i forgot to  add the user there so much shorter this way   and now what i can do is that i can kind  of group these different event handlers   together in the way that makes sense so for  example i could add here a slack event handler so create a file slack listener  then what i'm going to do is   import this post slack message that  i'm mainly going to use to do that and of course i need the event library and then first thing is let's add a function that   handles a user registered event and there  we're going to put the slack message to sales and of course don't forget to spam this  person there you go so there we have our   handle user registered event now i've got  an import here and i'm also going to add   a function that sets up the event  handling system for slack messages and it's going to call the subscribe method  that subscribes us to a particular type of event   and then user registered event should be  mapped to the handle user register event   function and now i can add more functions here  like when the user upgrades the plan etc etc   now in a similar way i can do that for for example  the system logs so add the log listener file   i'll just copy this over because i  need to do a lot of things the same and now i'm not posting a slack message  but i'm sending a system log event and i can do the same let's say i want  to have a password forgotten event like this and they add a  subscribe method goal here so i created these two collections of  listeners and in the observer i then should   import those and call this function so  that the event system is set up correctly so i'm not going to show how this works for  all the functions and all the methods in this   particular example but i made a kind of complete  version of this example where i rewrote this api   so that it now uses this event system that's also  in the same repository so if you check it out on   github you can look at it and play with it  yourself so you see we have here this event system   that i just added and we have a log listener with  a couple of different event handlers and we have   slack listener that looks the same and we have  an email listener that actually sends the emails   and the user and plan api access points now simply  post an event instead of using directly sending   emails posting logs or posting slack messages and  something really interesting happened here if you   look at the way that it's now structured and the  way that the imports are organized is that here   you see that the only dependency is on the event  system there's no longer any dependency on the   type of specific type of message or specific  type of things you want to do depending on the   event that occurs and the same is for the plan  so there is there's just the database stuff and   the event stuff that's it and the methods are also  becoming much shorter so we just have the database   operation and then we post the event if you look  at the listeners themselves you also see that the   number of imports is actually pretty low  it just imports event stuff that it needs   and since this is a slack listener it  needs the post slack message import   and the same for the log and for the email so  using the event system in this way has allowed us   to really separate the code much better if you  look at what we do in the actual test program   so we're simply initializing the event structure  by calling up these event handler setup functions   and then when i run this you see that it generates  a bunch of emails slack messages log messages etc   when i call these functions here and now it's  really nice that not only are the imports much   better structure that has less coupling because of  that some things are now much easier to change so   for example if i want to temporarily disable slack  messaging i could simply put this into comments   and if i run the code again there are no more  slack messages i didn't have to change anything   in my actual api functions because these simply  post an event and don't know anything about what   happens after that now similarly if let's say i  wanted to change from slack to a different message   provider like microsoft teams or whatever you use  in your company then also that would be a pretty   easy change i would simply disable this and then  add a new listener for my new messaging service   and enable event handling and i'm basically done i  don't need to change anything else in the code in   the previous version i would have to sift through  each of the functions in my back-end servers and   change the slack specific part to a teams specific  part so there you see a very clear example of how   reducing coupling helps you write code that's much  easier to change later on and this event mechanism   i said before it's really really useful for these  kind of things and a lot of people only use events   for ui stuff like handling a button click or  stuff like that but just keep in mind that if   you encounter this kind of structure or this kind  of thing in your code that events can really help   to clean it up you know on this channel i like  to talk a lot about writing code and designing   software i don't like to just provide recipes like  how to do x but i'd like to think a bit more about   why we're doing things the way that we do  them i've been teaching computer science   for over 20 years and i've started several  software businesses and simply following   recipes never really led to good results for me i  really needed the understanding that's behind it   on the other hand there are lots of tutorials out  there about design patterns and design principles   but often the examples are not very practical  what i discussed today was a solution to a problem   that i literally had a few weeks ago i hope this  video helps you think differently about how you   write code let me know in the comments below what  your thoughts are about this you can find a link   to github repository containing the code that i  worked on in this video in description below if   you're enjoying this series consider subscribing  thanks for watching take care and see you soon you
Info
Channel: ArjanCodes
Views: 76,748
Rating: 4.9491739 out of 5
Keywords: observer pattern tutorial, observer pattern tutorialspoint, observer pattern, observer design pattern, design patterns, observer pattern python, design patterns through python, design pattern in python tutorial, python observer pattern example, observer design pattern python, listener design pattern, listener pattern, observer design pattern tutorial, design patterns tutorial, observer pattern explained, observer pattern python 3, observer pattern example, gang of four
Id: oNalXg67XEE
Channel Id: undefined
Length: 15min 16sec (916 seconds)
Published: Thu Feb 18 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.