meeting -*- Outline -*- ** Observer or Publish-Subscribe or Delegation Event Model (23.9) another requirement for this iteration is making the graphical user interface work. Q: Why not just have the classes in the domain logic layer directly call the classes in the user interface layer? For example, have the Reservation class call the user interface directly? This would violate the separation of layers (model-view separation). We definitely want to have separation from the user interface, which changes a lot (Protected Variations). ------------------------------------------ OBSERVER OR PUBLISH-SUBSCRIBE (23.9) problem: How to allow events (state changes) to affect other objects, without coupling to them? How to support a one-to-many dependency between objects? How to separate layers that must talk to each other? Roles: A *publisher* or *subject* notifies other objects of an event. A *subscriber* or *observer* wishes to be notified of events. ------------------------------------------ Q: In our Reservation/UI example, which is the publisher? Reservation Q: Which is the subscriber? The UI classes ------------------------------------------ SOLUTION Solution: Define an "Observer" (or "Listener") interface, which documents the kinds of events and the information they carry. Subscribers/Observers implement the listener interface. Publishers have methods to dynamically register subscribers. Publishers notify all registered subscribers when an event occurs. ------------------------------------------ Q: Can there be more than one subscriber? Zero? ------------------------------------------ OBSERVER INTERFACE // $RCSfile: ReservationObserver.h,v $ #ifndef ReservationObserver_h #define ReservationObserver_h class Reservation; class ReservationObserver { public: virtual void update(Reservation *r) = 0; virtual ~ReservationObserver(); protected: ReservationObserver(); }; #endif ------------------------------------------ This defines the "events", here "update(Reservation *)". Can have more than one event type by having more methods other than update. The point of passing the reservation to the observer is that an observer may be listening or subscribing to more than one kind of event. ------------------------------------------ OBSERVER IMPLEMENTATION // $RCSfile: ReservationObserver.cpp,v $ #include "ReservationObserver.h" ------------------------------------------ For the publisher's role, we have to define and implement methods to allow subscribing to events, by adding objects of the ReservationObserver interface to some list. We also have to have a method to publish events, and we have to call that. ------------------------------------------ // $RCSfile: Reservation.h,v $ #ifndef Reservation_h #define Reservation_h #include "Money.h" #include "Flight.h" #include "ReservationObserver.h" #include class PricingStrategy; class Reservation { public: // ... Reservation(PricingStrategy *ps); virtual ~Reservation(); virtual void subscribe( ReservationObserver *ro); virtual void publish(); private: PricingStrategy *pricingStrategy; vector *flights; vector *subscribers; }; #endif ------------------------------------------ ------------------------------------------ // $RCSfile: Reservation.cpp,v $ #include "Reservation.h" #include "PricingStrategy.h" // ... void Reservation::enterFlight(Flight *f) { flights->insert(flights->end(), f); publish(); } Reservation::Reservation( PricingStrategy *ps) : pricingStrategy(ps), flights(new vector()), subscribers( new vector()) { } Reservation::~Reservation() { delete pricingStrategy; delete flights; // can't delete the observers... } void Reservation::subscribe( ReservationObserver *ro) { subscribers->insert( subscribers->end(), ro); } void Reservation::publish() { vector::iterator i; i = subscribers->begin(); for (; i != 0; i++) { (*i)->update(this); } } ------------------------------------------ Walk through how this executes. Draw the interaction diagrams, perhaps animate this using various people to play the roles of the objects *** Java version If have time, go over the Java code. Show figures 23.21-23.24 Differences from the C++ code: The Java version is more generic. The Java version is also of a "push" model, since the events contain information, like the total. *** discussion **** many subscribers are possible **** unsubscribing Q: In some applications in the subscribers will want to unsubscribe themselves dynamically. How would you support that? need to have a method in the reservation class to unsubscribe... **** event terminology this was originally called the Publish-Subscribe pattern. One publishes "events", which are just message calls (calls to update). **** not just for connecting user interfaces and models it's useful for decoupling two classes that need to talk to each other *** related patterns Observer is based on polymorphism. It provides protective variation in terms of protecting the publisher from details of the subscribers. Q: Why do want to protect against variation in the user interface? allows both the application and the user interface to be reused independently of each other lowers coupling in general