🎙 Develpreneur Podcast Episode

Audio + transcript

Patterns of Software Design

In this episode, we're discussing the Observer pattern in software design. We'll explore its intent, how it works, and its applications in graphical interfaces and event-driven programming.

2024-10-05 •Season 6 • Episode 190 •Patterns of Software Design •Podcast

Summary

In this episode, we're discussing the Observer pattern in software design. We'll explore its intent, how it works, and its applications in graphical interfaces and event-driven programming.

Detailed Notes

The Observer pattern is a software design pattern that allows objects to be notified and updated automatically when one object changes state. It defines a one-to-many dependency between objects, where one object (the observee) is responsible for notifying its dependents (the observers) when it changes state. The pattern works in pairs: the Observer and the object being observed. The Observer pattern is a simple mechanism that is easy to implement and understand. It is widely used in graphical interfaces and event-driven programming. The pattern is not a super common pattern, but it is one that is well-established and widely used.

Highlights

  • The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
  • The pattern works in pairs: the Observer and the object being observed.
  • The Observer pattern is a simple mechanism that is easy to implement and understand.
  • The pattern is widely used in graphical interfaces and event-driven programming.
  • The Observer pattern is not a super common pattern, but it is one that is well-established and widely used.

Key Takeaways

  • The Observer pattern defines a one-to-many dependency between objects.
  • The pattern works in pairs: the Observer and the object being observed.
  • The Observer pattern is a simple mechanism that is easy to implement and understand.
  • The pattern is widely used in graphical interfaces and event-driven programming.
  • The Observer pattern is not a super common pattern, but it is one that is well-established and widely used.

Practical Lessons

  • Implement the Observer pattern in your software design to improve its flexibility and scalability.
  • Use the Observer pattern to notify objects when one object changes state.
  • The Observer pattern can be used in graphical interfaces and event-driven programming.

Strong Lines

  • The Observer pattern is a simple mechanism that is easy to implement and understand.
  • The pattern is widely used in graphical interfaces and event-driven programming.
  • The Observer pattern is not a super common pattern, but it is one that is well-established and widely used.

Blog Post Angles

  • The benefits of using the Observer pattern in software design.
  • How the Observer pattern can improve the flexibility and scalability of software systems.
  • The Observer pattern in action: real-world examples and case studies.
  • The Observer pattern: a simple yet powerful software design pattern.
  • The Observer pattern vs. other design patterns: a comparison.

Keywords

  • Observer pattern
  • software design pattern
  • one-to-many dependency
  • notification
  • update
Transcript Text
This is Building Better Developers, the Develop-a-Noor podcast. We will accomplish our goals through sharing experience, improving tech skills, increasing business knowledge, and embracing life. Let's dive into the next episode. Well, hello and welcome back. We are wrapping up the season talking about patterns of software design. We are looking at one pattern that we skipped along the way. We're going to go back and pick it up. That happens to be the Observer pattern. We'll just dive right in, take a look at that, and then do a retrospective on the season to wrap it up a few minutes from now. First, the Observer. As always, we'll start with the intent as it was laid out in the Gang of Four Patterns of Software Design book many eons ago. Not quite eons, but more than a few years ago now. The intent is, quote, define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically, end quote. Now that probably is something that you can relate to, that you have worked with or even created that mechanism yourself. You may even know that it's an Observer pattern. Maybe not, because a lot of times the same thing would be called a Listener. Oh yeah, now things start clicking, right? Oh, Listener, I've used those quite a bit. If you've used any, really any graphical interfaces, it seems like over the last few years, if you've got something that's event-driven and it doesn't actually have to be a graphical control or application or something like that, you may have something event-driven and have or observers that are listening for something maybe in a data feed or some other processing or transactions that are going on. It's actually a pretty simple mechanism. It works in pairs. You've got the Listener and then you've got the Listenee. So you've got the Observer and you've got the object being observed if you look at the pattern using their lingo. And the way that you set it up, you want to be able to have as many observers as possible, or not as possible, as needed, as possible. Be a little bit resource intensive, but as needed. So for example, if you've got a control, keep this pretty common, and let's say it has a mouseover event, it has an enter event, it has an exit event, it has a click event, has a double click event, has a right click event. There's a whole lot of different things that can go on that you may want to have an observer observing. And you may want to have multiples for any given event. So a click, you may want to have one observer that actually has to deal with the data in the control. There's another observer that's watching that because it's going to adjust some of the other, it's going to make some graphical change. Maybe it enables or disables or something like that. And you don't want to do all those together. And you could, but it could be a situation where you don't want to. And honestly, what you don't want to do is pin yourself into a corner. You don't want to go in there and set up a design that suddenly you're sort of stuck. It doesn't have the flexibility that you need. So the way this is normally going to work is you have the observee, the object that's going to be observed, and it has a way to register or announce or something along those lines to be observed. And so when an observer class is instantiated, that observer object will talk to the observee and basically say, hey, I'm observing you. And then the observee, whenever the event happens, it's going to have a list of things that's or collection, it doesn't have to be a list, but a collection of observers. And when that event is triggered, it's going to tell all the observers, hey, the event was triggered. So an on click, very common one, you could have three or four different observers listening for waiting for that click event. When you click, you know, take your mouse and you click on that object, it will say, oh, I've been clicked. And then within that, it says, well, now I need to find my collection observers listening for this and tell them that this has occurred. And then they can go do whatever they want to do about that. Maybe nothing. Maybe they do quite a bit of work. Doesn't matter. The observee doesn't care, doesn't really know anything beyond somebody's observing me. And when this happens, I want to make sure that they are informed. And it could be several someones or several some things if you want to be a little more precise. And in that case, it's going to go through again, it's collection, notify all of them. And it's easy to do if you think about this from an interface point of view, it's actually fairly easy. You're going to have a register interface and maybe a deregister probably as well on the observee. It will implement the observable pattern, let's say. And I think there's some frameworks that do it exactly this way. So you'll have an observable pattern or sorry, observable interface. And anything that implements the observable interface will have to implement a register and a deregister. And then for the observers, they're going to have an interface as well that will be, we'll call it like do or action or trigger or something. I have triggers of reserve, we probably want to avoid that. So maybe action or update. And what happens is the observee as part of the register is going to allow it to with the registration process, you pass it an object of observer class, of observer interface. And we know because of that interface that we're going to have a method call for let's say action. So when something's triggered in the observee, it just goes through its collection of observers and calls action on each of those. And maybe it sends some information, maybe not. It just sort of depends on how you want to do that. Maybe it's going to send a value, but not necessarily depending on how you set stuff up. Particularly if you go to a graphical user interface, controls and objects, there's a lot of times that you really don't need to send a whole lot of information. You just need to know that something was clicked or something was entered or something was exited. You don't need to know necessarily where like maybe coordinates or values or anything like that. But maybe you do. And like as an example, sometimes on a mouse click, actually, I'm sorry, not mouse click, but you think about a keyboard press, then a lot of times you wouldn't want not just the keyboard to press, but you would want to know what was the key that was pressed. So that key press event is going to send a value. But generally speaking, the observer and the observee relationship, it's going to be an action and then that is part of the interface on the observer side is that that action that it implements will either be, you know, you'll know by its signature that you need to either send it a value or not. So when you're actually implementing it, it's again, it's maybe one of the more complicated ones as far as just number of classes, players involved in the pattern, but it's not a very complicated relationship among them. You send again, sort of like with the visitor pattern that we've talked about, you have sort of two hierarchies. You've got the observer, which starts with that interface, and you've got the observee, which starts with it or observable, which starts with its interface. Then from there, you can have all kinds of concrete implementation and you can stack them up. So you could have, like in the prior example, you could have a whole bunch of different concrete observers that part of their action is that they take no value. There's no value. It's just, hey, this occurred. And then you could have a whole other round of them that there's a value set and maybe in some cases it's a string, in some cases it's an integer or float or Boolean or whatever it happens to be. So you see this could get, it could get to be a sort of deep and complicated hierarchy depending on what it is you're modeling. Now from personal examples, when I've done event driven applications where you've got a lot of like mouse events and keyboard events, maybe some clicks or some of the other things that go on, enters and exits and hovers and stuff like that, you could actually have quite a few observers out there and quite a sort of like families of hierarchies of them as well, which can be hierarchically a little complex. But the implementation, remember we're down to, it's really register, deregister and action. So it's not big fat classes with a lot of stuff on top of them. They should stay typically pretty thin. And that is besides just from general design, they should be thin. You also want them to be because you don't want to have a whole lot of objects floating around that are these big things that are just waiting for something to happen. If your observers are huge, then you've got this collection of huge objects that are going to exist while everything's waiting for whatever the event is to trigger the action. And that gives you, I think, a pretty good synopsis of how event driven programming of any kind will work out, or at least an approach, a good pattern for it. Is it you register, something happens, it tells the observer, the observer does something and that's it. The observable has no knowledge of its observers other than the registration. They're not going to send, other than through the action, they're not going to send anything back. They shouldn't. You want to keep it very thin and very loose so that you can put whatever kind of observer on the observable that you want. As far as implementing this in a given language, once again, we're dealing with really just interfaces and inheritance. So this is going to give you pretty much the exact same structure, regardless whether you're C++ or C sharp or Ruby or Java or whatever your favorite language does your happens to be. The challenge of the week, before we get into the summer, the season, I want to do one with this. It's just a week for the observer. And really to just think about this is where we're wrapping things up. Go out there and take a look at your frameworks, particularly a graphical or stream processing framework. If you've got any of those that you're working with, take a look at them. I bet you'll see an observer. You may even see the observer name used as part of the object classes. I've seen that a couple of places. I wouldn't be surprised if you don't run into it. So this is a pretty, it's not like super well used, but not super common in its use, but it is one enough that it has a very typical or standard type of use across a lot of different places and locations. It's one of those patterns that almost, I think even if you didn't know that the pattern existed, you would see the pattern because it's just, it's done this way so many different times and in so many different similar yet different situations. Now we'll push that one aside and now let's step back a second and look at the season as a whole. We want to start with what have we learned? Well one of the things we've probably learned are there are names to things that we've been doing that we probably didn't know. I doubt most of you, unless you've gone through the book, you probably have, there's probably some patterns that we've talked about that the name itself was new to you. The fact that what it described, what that name described, that label was new, but actually what it did was not. You had done something like that before. Maybe you'd never heard of an iterator called an iterator, but you've done that before in some sort. Even here, maybe you didn't know an observer was an observer. Maybe you, like I said, maybe a listener or something like that, but you've been exposed to it. So you're not really getting something new necessarily going through the patterns and looking at them and understanding them, but you are sort of seeing best practices, particularly if you go deeper, if you go a level deeper, if you get the book and read through it, if you go through a lot of the examples or look at some, there's some other sites that go pretty deep into some of these patterns as far as implementation techniques and gotchas and things like that. What they do give us are best practices for some very common algorithms or approaches, solution approaches to the problems that we see across all sorts of different languages. And that is something that is going to make us a better developer. Is it instead of us reinventing the wheel, we can take a look at the patterns that are out there that apply to our problem domain and apply them accordingly, correctly. There may be some gotchas or some missteps or errors that we would ourselves come up with, but once we can take a look at the pattern and see how it's done and what it suggests, that may help us to get a little higher quality, a little faster. And it's also a, I think it's very valuable for us to have ourselves, our work validated to know that yes, this is a problem that other people have seen and the solution we came up with is pretty much the same as what other smart people came up with. Smart enough to write a book if you think that writing a book suddenly makes you smart. So that makes these patterns, they're like morale boosters, but they are also really good things to have in the back of your head as ways to, you know, it's again, it's a pattern. It is a template. It's just like if you have a template for your weekly status, you're going to get your weekly status done faster because you just fill that in. You could do that with, just think about anything, any reports or things that you do on a regular basis that you're, any communications you do. If you've got a template, if you've got something that's a guide to help you through it, you're going to get done faster. And it's not necessarily because it's, because you couldn't do it yourself. It's just because it's less thought on your part to walk through it. You get to focus on performing the steps instead of thinking about what are the order of the steps of this process. And that's what patterns will give us. So that adds a couple of insights. By and large, what they're going to give us is a faster, cleaner, best practice approach to solving some of these problems. So the challenge of the season that I want to throw out at you is take a look back through these and maybe just like make several notes somewhere. You don't have to use all of the patterns, but maybe take a half dozen that really seem to apply and just make some notes somewhere or do something so that those, I don't necessarily have to say in the front of your mind, but they stay fresh enough that when you're thinking about designing a solution, those are very handy. I think of it as having a, we probably all have this, where you've got a desk or a drawer somewhere in your house that you've got a couple of screwdrivers in because screws come loose all the time. And maybe you have a couple. Maybe you've got eyeglasses, you've got like a little eyeglass repair kit sitting around that's pretty handy. Or maybe you've got a lot of like light sockets or electrical sockets that those little screws come loose a lot or you name it. But you probably have a situation where you've got a tool that you keep pretty handy so that if you need it, it's right there. I think that's what you want to do with the patterns is have it in a way that they're familiar enough that you can sort of reach out, pull that thing off the shelf and dust it off and use that pattern to solve a problem and push you through to the solution a little faster or at least push you through to the new parts of the problem. And you'll find that a lot that whatever you're trying to solve, there's going to be some stuff within that solution that are, we'll call them mini problems, M-I-N-I not M-A-N-Y, miniature problems that you have to solve and you'll find that you end up solving the same problem over and over again a lot of times. Those little miniature problems show up all the time. Opening a file, closing a file, writing something to a screen, for loops, while loops, do loops, that kind of stuff. Those little miniature things, if you've got a pattern for it, then hopefully it'll allow you to like fly through those and get to the new part of the problem that you need to solve. And that will wrap up the season. I really want to thank you for hanging out through all of these. I do find it very useful. This is a design patterns book came out in the early 90s. And since then, I had a period probably about five or six years I'd read it and then put it away. But since then, probably every couple of years, I'll crack it open and flip through it. It's not like I read it cover to cover, but just sort of flip through it and look at what are the intents and remind myself what the patterns are. So I have found that very valuable and I wanted to pass that on. We've done some other posts. We had a couple mentor sessions where we talked about patterns and even anti-patterns. It may come back and do a season on anti-patterns at some point. I think those are at least as valuable as patterns, maybe even more so because I think anti-patterns are a lot more in your face, red flag kind of things that we stupid mistake. Well, not stupid. It's sometimes stupid, mostly ignorant mistakes that we make on a regular basis where it's good to have something that reminds us that, oh yeah, that's not a good way to go. That is not going to work well. As always, you can reach me email, shoot at info at developerneur.com. We've got the Facebook page. Follow us on at developerneur. We will have another season coming out, come out right around the corner. I'll probably take a little break, do a couple of best of, not really best ofs, I guess, but just some like quick hit kind of thoughts. And then we will dive into season eight and I'll do that on a, I'll do that on a serial basis. I'm not going to run parallel seasons again. I did at this point because it just had a couple of things I really wanted to knock out and now I'm going to try to keep these a little bit more or run seasons and I'll talk a little bit more about this, but I'll probably run seasons for a month or two, something like that and go through a topic and then, or a series of topics and then move on to the next one. But that will wrap it up for now. So go out there, have yourself a great day, a great week, and we will talk to you next season. Thank you for listening to Building Better Developers, the Developer Noor podcast. For more episodes like this one, you can find us on Apple Podcasts, Stitcher, Amazon, and other podcast venues or visit our site at developernoor.com. Just a step forward today is still progress. So let's keep moving forward together.