Here is a very common problem with an elegant solution. Suppose you have
a complex class, with lots of data structures, and you have defined several
views on it, for example as X11 widgets:
The display classes would store a pointer to a FOO object, and when
Display() is called they would draw into an X11 pixmap a picture of the FOO
object ( say a graph, or some text ). Now every time the foo object changes
you want the picture to change also. You could just call Display() every
time a call is made which changes the object:
Of course now you have to be very careful about how many views on FOO objects
are open at any time. There may be multiple instances of a view around at
one time, or even instances of different types of views. It may even be that
the number and types of views on an object are not predictable. In a nice
GUI interface users may want to select the number and kinds of views they
want open at any time. To support these contingencies you would need to
store pointers to the views that are open at any time and update them all.
For example:
Now this is a more workable solution. The FOO class does not have to know anything about which views are on it at any time, and the code does not make assumptions about how many views are open at any time. But it has some heavy overhead. Every time code is written to open a new view on a FOO you have to remember to register the view in the global view containers. In this case we made only a single container for each view class, but if you had many instances of FOO objects, either you'd want to make view containers for each instance or you'd have to put up with updating views on all your FOO objects every time any single one was updated. And god forbid you should want to add a new type of view class, you'd have to go out and make a new view container, and add new Display() calls all over. Luckily we can do better.
Clearly it would be better if each instance of a FOO object knew
which views were pointing to it. And it would be nicer if we could store
all the different types of views in the same container. We must be careful
how we do this because we don't want the FOO class to have to know anything
about views. It should concern itself with its own foo data and not have
to know about X11, or users or anything. All of this can be done with a
single base class from which FOO will inherit from, and a few wrapper
functions for the Display()'s:
The MODEL_VIEW base class allows its derived progeny to have a container
with pointers to the views on the object, and a container with pointers to
a 'notify' function for each view. The view objects are stored as void
pointers so the derived class does not have to know anything about the views
themselves. Basically each time an instance of a view is allocated for an
instance of the derived class ( in this case a FOO object ), the viewed
object is just passed two pointers. It knows nothing about them. Its just
told: every time you update, call this magic function on this magic pointer.
All the programmer has to do every time a function is coded that changes
a FOO object is call the base class ChangedNotify() function:
Now FOO objects don't have to know anything about the types of view
classes referring to them, but the views have to remember to register with
their viewed object. So in the constructor for each view you must register
the view and in the destructor you must un-register:
This system gives you a whole bunch of nice features: every time FOO objects are updated, the displays are automatically updated. In the above implementation there is even a facility for telling the display function how deep the change is. It may be that for minor changes some views may not wish to update... but other views may wish to update for any change. Only instances of FOO that have changed will be updated. There is no need now for the programmer to keep track of which views are on which objects, this is done automatically in the view constructors and destructors. All the different types of views are stored in the same container for each instance. The costs for this system are also small. Each viewed class need only derive from 'MODEL_VIEW'. Each view must register itself in the constructor and destructor, and the display functions must be put into wrappers that match the calling convention of the 'NOTIFY_FUNCTION' typedef. And no changes are needed to the calling code. Once the objects and the views are correctly set up the programmer need not trouble with updating views at all.
The MODEL_VIEW class can also be extended to provide other services. For example one could add a container with pointers to functions that will warn the view of the caller's impending destruction. Allowing them to exit gracefully before they are caught with a dangling pointer after the caller is destroyed.
Since the viewed class does not have to know anything about the
views on it, they can be almost anything. For example a view may be not just
an image or text representation, but it may be a link to a database that
wants to be updated every time the object changes. This programming technique
of using stored pointers to functions that were passed in to an object from
outside is known as a 'call-back' and can be used for all kinds of tasks.