![]() |
Implementing the observer pattern in C
An "observer" is an object that wants to be notified when something of
interest happens in another object. Practically, within the context of the container library, an observer is a callback that is called when an operation for which the observer has subscribed happens. The observer interface looks like this: typedef void (*ObserverFunction)(void *ObservedObject,unsigned operation, void *ExtraInfo[]); typedef struct tagObserverInterface { int (*Subscribe)(void *ObservedObject, ObserverFunction callback, unsigned Operations); int (*Notify)(void *ObservedObject,unsigned operation,void *ExtraInfo1,void *ExtraInfo2); size_t (*Unsubscribe)(void *ObservedObject,ObserverFunction callback); } ObserverInterface; extern ObserverInterface iObserver; The process goes like this: 1) You subscribe to a container requesting to be informed about a set of operations you want to observe. 2) The container notifies you when one of the operations that you subscribed to happens. 3) You unsubscribe when you are going out of scope or for whatever reason. Note that the container can unsubscribe too if it is going out of scope. Note that the notifications gives to the observer the maximum information possible so that there is no need to perform costly operations to retrieve it. Each operation (Add, Erase, Replace, etc) will pass a different set of extra information to the observer. The implementation is already at http://code.google.com/p/ccl. When an object has no observers, there is still a small overhead to pay: the test of a bit in the "Flags" field. I haven't been able to eliminate that "overhead", and that means that all operations that change the container have this test. When a message to an observer should be sent the overhead is bigger than that since the observer must search for all occurrences of the object in the observer table. The observer table associates objects with their callbacks. An object can have several callbacks and a callback can have several objects. At this stage I would like to have (if possible) feedback on the design of this. Am I missing something fundamental? Thanks in advance. jacob |
Re: Implementing the observer pattern in C
Le 28/03/11 00:25, christian.bau a écrit :
> On Mar 27, 10:28 pm, jacob navia<ja...@spamsink.net> wrote: > >> At this stage I would like to have (if possible) feedback on the design >> of this. Am I missing something fundamental? > > You should have a look at Apple's CoreFoundation library. I had. <quote> A CFNotificationCenter object provides the means by which you can send a message, or notification, to any number of recipients, or observers, without having to know anything about the recipients. <end quote> Yes. The same thing in my interface. <quote> A notification message consists of a notification name (a CFString), a pointer value that identifies the object posting the notification, and an optional dictionary that contains additional information about the particular notification. <end quote> 1) I send the object that sends the notification of course 2) I do not use string names but bits to convey the same information 3) I have the same extra information. <quote> To register as an observer of a notification, you call CFNotificationCenterAddObserver, providing an identifier for your observer, the callback function that should be called when the notification is posted, and the name of the notification and the object in which you are interested. <end quote> No identifier is needed since there is no observer object, just a callback. The callback is just its address. We are in C. Of course the callback could be part of another object/structure/whatever. This doesn't interest anyone. <quote> The observer identifier is passed back to the callback function, along with the notification information. You can use the identifier to distinguish multiple observers using the same callback function. <end quote> If you subscribe multiple times the same callback with different flags the callback will be called with different data. Since the name of the operation (as a bit of a set) is passed, the callback can know which operation actually occurred without any additional overhead. Note that my design has the same functionality with less overhead. <quote> The identifier is also used to unregister the observer with CFNotificationCenterRemoveObserver and CFNotificationCenterRemoveEveryObserver. <end quote> To unregister you call Unregister with: size_t (*Unsubscribe)(void *ObservedObject,ObserverFunction callback); If ObservedObject is NULL, all callbacks to the given callback function are destroyed. If callback is NULL, all observers that have the given objects are terminated. If both are non null, only the matching callbacks afre destroyed, regardeless of which operations they follow. I think that there is less overhead my implementation but I am of course biased :-) |
Re: Implementing the observer pattern in C
I think it is easier to understand when you read the doc. Here is
an excerpt of the essential parts of the doc: The observer interface --------------------- When a container changes its state, specifically when elements are added or removed, it is sometimes necessary to update relationships that can be very complex. The observer interface is designed to simplify this operation by allowing the container to emit notifications to other objects that have previously manifested interest in receiving them by subscribing to them. This interface then, establishes a relationship between two software entities: 1. The container, that is responsible for sending the notifications when appropriate 2. The receiver, that is an unspecified object represented by its callback function that is called when a change occurs that matches the notifications specified in the subscription. Since this relationship needs both objects, it will be finished when either object goes out of scope or breaks the relationship for whatever reason. Both objects can unsubscribe (terminate) their relationship. The interface ------------- typedef void (*ObserverFunction)(void *ObservedObject, unsigned Operation, void *ExtraInfo[]); typedef struct tagObserverInterface { int (*Subscribe)(void *ObservedObject,ObserverFunction callback, unsigned Operations); int (*Notify)(void *ObservedObject,unsigned operation,void *ExtraInfo1,void *ExtraInfo2); size_t (*Unsubscribe)(void *ObservedObject,ObserverFunction callback); extern ObserverInterface iObserver; ObserverFunction typedef void (*ObserverFunction)(void *ObservedObject, unsigned Operation, void *ExtraInfo[]); Description: This function will be called by the interface when a notification is received for an observed object. The call happens after al arguments have been processed by the actual work of the function and they are in a consistent state. For the callbacks that are called when an object is deleted from a container the call happens before the call to free() and before any call to a destructor (if any) is done. Arguments: 1. ObservedObject: Specifies the object that sends the notification, i.e. the container that has the subscription. It is assumed that this container conforms to the iGeneric interface. 2. Operation: The operation that provoked the notification. Since it is possible to subscribe to several operations with only one callback function, this argument allows the callback to discriminate between the operation notifications. 3. ExtraInfo: This argument is specific to each operation and conveys further infor- mation1 for each operation. None of the arguments will be ever NULL or zero. Subscribe int (*Subscribe)(void *ObservedObject, ObserverFunction callback, unsigned Operations); Description: This function establishes the relationship between the observed object (argument 1) and the observer, represented by its callback (argument 2). The third argument establishes which operations are to be observed. This operation performs an allocation to register the relationship in the observer interface tables, therefore it can fail with an out of memory condition. Notify int (*Notify)(void *ObservedObject,unsigned Operation, void *ExtraInfo1,void *ExtraInfo2); Description: This function will be used by the container to send a message to the receiver callback. The arguments correspond roughly to the arguments the callback func- tion will receive. This function will call all the objects that are observing ObservedObject and that have subscribed to the operation specified in the Operation argument. This implies a search through the observer interface table, and possibly several calls, making this function quite expensive. The time needed is roughly proportional to the number of registered callbacks and the complexity of the callbacks themselves. Unsubscribe size_t (*Unsubscribe)(void *ObservedObject, ObserverFunction callback); Description: This function breaks the relationship between the observed object and the observer. There are several combinations of both arguments: • The ObservedObject argument is NULL . This means that the callback object wants to break its relationship to all objects it is observing. The observer interface will remove all relationships that contain this callback from its tables. • The callback argument is NULL . This means that the given ObservedObject is going out of scope and wants to break all relationships to all its observers. The interface removes from its tables all relationships that have this object as the observed object. This happens normally immediately after the notification FINALIZE is sent. • If both callback and ObservedObject are non NULL , only the matching relationships will be removed from the tables. |
Re: Implementing the observer pattern in C
Le 28/03/11 17:41, Scott Fluhrer a écrit :
> In my experience, when a routine registers a callback, it's often useful if > that routine can specify a 'context' parameter so that when the callback > occurs, the callback routine has some, well, context. > Yes, I thought about that, and I do send a lot of context. The callback receives (1) The address of the concerned container (2) The operation code (3) Up to two extra arguments further furnishing the new or deleted object from the container and its index. With this context I think that the callback has all elements to do its job, whatever that may be. But maybe I forgot something. > One example that might apply to this specific interface might be if we're > observing data structure A so that we can keep data structure B sync'ed to > it; it might be useful if the callback has a pointer to B (especially if > there are several different A's we're observing at the same time). > Ahh... right, now I see your point. I did not think about that. I have to try to keep the thing simple. If I implement it, it would be another argument to Subscribe and another argument to the callback... Will think about that. > This can be done by adding a (void *) parameter to the Subscribe routine, > which is passed to the ObserverFunction. It might also be wise to have the > caller pass it to the Unsubscribe routine (so that if two different > observers registered to the same object, that's another way to specify which > is shutting down; a tad obscure, but it might be useful). There is an explicit "Unsubscribe" routine. |
| All times are GMT. The time now is 04:02 PM. |
Powered by vBulletin®. Copyright ©2000 - 2013, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.