The Observer Design Pattern in Cpp - Mike Shah - CppCon 2022

Ғылым және технология

cppcon.org/
---
The Observer Design Pattern in Cpp - Mike Shah - CppCon 2022
github.com/CppCon/CppCon2022
Games, desktop software, phone apps, and almost every software that a user interacts with has some sort of event handling system. In order to handle events, a common behavior design pattern known as the 'observer pattern' allows one or more objects to monitor if a change of state takes place in another object. In this talk, we are going to do a deep dive into the behavioral design pattern known as the observer. The pattern utilizes a Subject and Observer (or publisher and subscriber) model to notify when state has changed from the subject to one or more observers in order to help make our software more maintainable, extensible, and flexible.
I will show some examples of the observer in modern C++ as well as real world use cases of where observers are used for further study. Finally, I'll discuss the tradeoffs of the observer pattern, and discuss which scenarios you may not actually want to use the observer pattern. Attendees will leave this talk with the knowledge to go forward and implement the observer pattern, as well as how to spot the observer design pattern in projects they may already be working on!
---
Mike Shah
Mike Shah is an Associate Teaching Professor at Northeastern University in the Khoury College of Computer Sciences. His primary teaching interests are in computer systems, computer graphics, and software engineering. His research interests are related to performance engineering (dynamic analysis), software visualization, and computer graphics. Along with teaching and research work, he have juggled occasional consulting work as a 3D Senior Graphics Engineer in C++.
Mike discovered computer science at the age of 13 when googling ”how do I make games”. From that google search, Mike has worked as a freelance game developer, worked in industry for Intel, Sony Playstation, Oblong Industries, and researched at The Ohio Supercomputer Center to name a few. Mike cares about building tools to help programmers monitor and improve the performance of realtime applications- especially games. In Michael’s spare time he is a long distance runner, weight lifter, and amateur pizza maker.
__
Videos Filmed & Edited by Bash Films: www.BashFilms.com
KZread Channel Managed by Digital Medium Ltd events.digital-medium.co.uk
#cppcon #programming #cpp

Пікірлер: 21

  • @zechordlord
    @zechordlord Жыл бұрын

    This is a very flexible pattern but also a huge footgun in complex systems. The core issue is that you have no knowledge of the state of the observers when notification is done. Cyclic notification, overnotification, deadlocks, lifetime issues (crashes or memory leaks), all hidden in pretty deep stack traces are very common when using Observer. In my opinion a better pattern is accumulating those messages and processing them in bulk at certain points of the program when a system is in a better known state (discussed in the game programming patterns book as asynchronous or "lazy" notification if I remember correctly). Otherwise one needs to be very conservative about how observers process notifications. Trivia: The software shown in the Blender slide is actually quadriflow, though blender itself does use asynchronous notifications in some of its subsystems. The unsafe property of the Observer pattern can be easily seen when trying to implement it in the Rust language, where it can easily fail if done naively, since we need to store a reference of the observer in the observable.

  • @JarOfHeat100
    @JarOfHeat1005 ай бұрын

    this is one of the best lesson on design pattern i've ever seen. practical and interactive. great job!!

  • @MarekKnapek
    @MarekKnapek Жыл бұрын

    Things to consider when designing this kind of notification system: 1) When I'm watcher1 subscribed to subject1 and to subject2, how do I know which subject is notifying me? 2) Is additional data coming with the notification? 3) Can the watchers affect the notification before it is send to other watchers? (Think of "can I close the app, is there unsaved document" type of event.) 4) Adding and removing (and destroying) watchers as reaction to notification. 5) Notification being fired as reaction to another notification, nesting, infinite chain of notifications. This is only from top of my head (learned some of those points the hard way).

  • @carlosgil2691
    @carlosgil2691 Жыл бұрын

    In my opinion, the use of shared pointers for this pattern is a code smell. If you are forced to use a shared pointer, there is a problem with the application design; there may be missing levels of abstraction. If the object lifetimes arent clear, then a weak pointer is preferable. Also, the inheritance paradigm still couples the observer to the subject. One way to resolve this is to use std::function, which I've found quite useful when working across libraries.

  • @dimatrushin8516

    @dimatrushin8516

    Жыл бұрын

    It is definitely a mistake. A part of the observable-observer protocol is to unsubscribe at the moment of death. If observable dies it unsubscribes all the observers. If the observer dies it unsubscribes from the current observable if any. Thus there is no need in the shared_ptr in any way shape or form. The way it is presented in the video is a design mistake. Because now observable controls the life-time of each observer subscribed to it.

  • @MatkatMusic
    @MatkatMusic Жыл бұрын

    Another name for this pattern is the "Listener" pattern. the JUCE framework (audio development framework) uses that term, instead of 'Observer/Observable'.

  • @broken_abi6973
    @broken_abi6973 Жыл бұрын

    I think that one of the main traps of this pattern is when people try to overgeneralize the use-cases handled by a single subject instantiation. The speaker tried to show how to include different "systems" in a single subject (multiple frameworks do this as well), but I think that this should be a last resort. The code gets much cleaner and easier to manage when you have different subject instantiations for different systems or use-cases. You can more clearly see which system each class in your codebase depends on. You can better control the scope of each subject. You can also then templatize each subject to pass actual data to the observers.

  • @benjaminshinar9509
    @benjaminshinar9509 Жыл бұрын

    I understand that this was designed for beginners, but I really wish the implementations were more moderns and less Java. drop the virtual inheritance in favor of inheriting from multiple template and employ the "using declarations" syntax. (and use the visitor design pattern and std::variant for type safety).

  • @mohamedfares7135
    @mohamedfares71357 ай бұрын

    Beginner questions if one of you would mind clearing out: 1) in the UML, the observer holds a reference to the subject yet it was not shown in the implementation unless I missed something? 2) why was there a reference to the subject in the observer instead of a reference to the Isubject incase the use wants to subscribe to different subjects? 3) shouldn't the observer has a method to subscribe to different subject, maybe the observer should have a list of references to Isubject? Thank you for taking the time and sharing from experience.

  • @mrandersson2009
    @mrandersson2009 Жыл бұрын

    Qt's signals and slots: "Am I a joke to you?"

  • @dimatrushin8516

    @dimatrushin8516

    Жыл бұрын

    The reason why you are asking this question is because the implementation of the pattern in the video is, lets say, awful. These should have been the main key differences: 1) Each observer must support at least three types of notifications: OnSubscribe, OnNotify, and OnUnsubscribe. They could be the same but you must notify the observer when it is subscribing, because you want the observer to be updated and to become in a coherent state at the moment of subscription. For example, if you connect the view to the model you want the view to show the sate of the model immediately. 2) Also, the observer observable relationship guaranties that each observer is subscribed to at most one observable at each moment of the time. 3) All connections are done via observable method subscribe and nowhere else. Now, Qt signals/slots. 3) In Qt you have a global function connecting a signal with a slot. So, anyone in any place of the program may connect a signal and a slot. Good luck with tracking those connections. 2) There is no restriction on the connection type. For example, you may have several parallel connections, that is, you may connect the same signal with the same slot several times. Or you may connect a slot to several signals. It is painful if you need to distinguish the origin of the signal. 1) And the best part. The signal does not notify the slot at the moment of connection. So, you cannot update the recipient of the information at the moment of connection. And it is impossible to do that. Because you connect the signal and the slot from the outside of the object by a global function. But in order to notify the slot, you need to know the state of the object with the signal and its internal logic to notify with the actual data. So, when you connect a model with a view via Qt signal/slot connection you have to update the view somehow. In my opinion, you should implement observables and observers as better versions of signals and slots. They must be special members of the class that provide connections of specific types with specific safety guaranties.

  • @oysteinsoreide4323
    @oysteinsoreide43234 ай бұрын

    a std::function is still an object, but you think of it as a function. It has a function like behavior.

  • @oysteinsoreide4323
    @oysteinsoreide43234 ай бұрын

    I guess it will be problematic to make the observable pattern in the standard as each use cases may have different requisites, so to make it general, it will mandate a very general approach. Making the observer pattern by hand is quite easy, and for the lifetime, it will be the same problem if the library is general. Because a general pattern will not be flexible if they enforce shared_ptr or unique_ptr and so on. So there is no free lunch here. The lifetime of objects will be something you need to control in some way regardless of having a standard library that supports the pattern.

  • @LucasHartmann
    @LucasHartmann Жыл бұрын

    Maybe concrete classes should HAVE-A observable feature, or more, probably templated with event arguments. Deriving observables from an interface feels unnecessary, as there is no need to refer to them in an abstract way.

  • @nijuyonkadesu
    @nijuyonkadesu Жыл бұрын

    noice

  • @nabilandadamslaboratory3422
    @nabilandadamslaboratory34227 ай бұрын

    Where can I find the code?

  • @TristanWalter
    @TristanWalter Жыл бұрын

    I am happy for him that he‘s so happy, and he‘s a great presenter otherwise I think - but his frequent laughs seem out of place somehow.

  • @kamanashisroy
    @kamanashisroy Жыл бұрын

    Guys, C++ can do things at compile-time .. Sometimes that can give speed. class SoundObserver { using SUPPORT_SOUND = true; }; class VisualObserver { using SUPPORT_SOUND = void; }; template class ProcessSound { TO observer; void notifySound(Bla soundEvent) { if(is_void(TO.SUPPORT_SOUND)) { return; } observer.playSound(soundEvent); } };

  • @indranilbanerjee1494
    @indranilbanerjee1494 Жыл бұрын

    Disappointing. Talks like this should be show casing best practices. Don't use raw pointers and enum in the slides and mention the better ways as an aside 😞. Also I think a weak_ptr is probably the best to hold the observers. A shared_ptr could keep the object alive when no one else cares about it.

  • @nijuyonkadesu

    @nijuyonkadesu

    Жыл бұрын

    if he had shown us the how threads and locks are used along with weak pointers, would you be satisfied ??. As of my observation, the audience, they seem to be interested and listened to this basic implementation very well. I wonder what could have been their reaction if Mike started with complex stuffs from the beginning...

Келесі