Re: Persistence and supercalls under BlackBox

From: [at]} <Wolfgang.Weck{>
Date: Mon, 15 Feb 1999 22:25:10 +0200 (EET)

> I read Wolfgang's Graph again and discovered he works around the problem
> by a little bit of cheating:
> MODULE GraphModels (my own translation);
> Figure = ABSTRACT RECORD (Stores.Store) l,t,r,b END;
> Model = RECORD (Models.Model) (*list of Figures*) END:
> END GraphModels.
> Now, he does not have to supercall Figure's store/load because
> in Graph it is the *model* that stores l,t,r,b's of all Figures,
> and therefore Figures do not have anything to store/load,
> and therefore their store/load methods do not need to be
> supercalled! But that is cheating, not a general solution.

Tough words! :-)

Hm, maybe, I need to point out the true reason for having the model
externalizing and internalizing the bounding box data (that's what l, t, r, b
is). As a result the type Figure does not have any persistent attributes
indeed, but this is a sheer coincidence.

The one and only reason for having the model handling the bounding boxes is
the wish to handle alien figures in a reasonable way. Alien figures occur

whenever one tries to load a document containing a figure type that cannot be
instantiated, typically because the respective module is missing. (We are
talking about an exercise in extensible systems here, remember?) In these
cases, the figure cannot be displayed properly, because the system doesn't
know what it looks like. Thus, it shall display a grey box of the size of the
figure. For that it needs to know the bounding box of the figure that could
not be internalized and thus doesn't know about the bounding box itself. So we
need to make sure that the model gets this data, even if the object could not
be instantiated. Plain and simple reasoning. No cheating.

> In general, the base class will have some data fields (or else
> there is no need for a base class), whose storing/loading
> should be the responsibility of the class itself, not of its container.
> In my case I have 3-level deep class structure: I am extending
> Wolfgang tutorial Graph with an intermediate Object who holds
> colors, line type, etc, etc. At the concrete level one below Object
> I have Curve and Histogram that use Object's common data fields
> and add their own fields. Wolfgang solution does not work
> anymore.
> Thus, my question is: if not supercalls, then what?
> If OuS do not propose their concrete alternative to supercalls,
> then their recommendantion not to use supercalls does not
> make any sense.

We have here -in different cases- used different patterns. Here is my prefered
one. Let's stay with the Graph example, but assume that Figure had some state
to externalize. Figure would define new, typically abstract, methods, e.g.
ExternalizeFigure / InternatlizeFigure. In addition it would implement
Externalize / Internalize as non-extensible methods to handle its own data and
call the new abstract methods.

        Figure = POINTER TO ABSTRACT RECORD (Stores.Store)
                x: INTEGER

        PROCEDURE (f: Figure) ExternalizeFigure (VAR wr: Stores.Writer), NEW, ABSTRACT;

        PROCEDURE (f: Figure) Externalize (VAR wr: Stores.Writer);
                wr.WriteInt(f.x); f.ExternalizeFigure(wr)
        END Externalize;

and something similar for internalization...

Hugh, complicated, isn't it? And lot's of cheating! Inventing new abstract
methods just to avoid supercalls!


Well, let's see. What can go wrong with the good old super call? In the
example at hand, the extension programmer can either simply forget to
implement the externalization code at all (not very likely but happens
occasionally - at least to me), or he can just forget the super call. In both
cases the first test will probably exhibit the problem, and we all being
clever programers we will immediately guess the reason of the problem, fix it,
recompile and test again. (And thanks to the fast compiler, that doesn't even
take a lot of time.)

With the above pattern we can even safe the time for the testing. If the
extension programmer forgets to implement ExternalizeFigure, the compiler
complains. (Remember, I said ABSTRACT, not EMPTY!) And the super call is
unnecessary and thus cannot be forgotten. By making the Externalize no further
extensible, it cannot be overwritten incidentally.

In that way, the above code pattern reliefes a client programmer from some
debugging. Even more: it is better documenting. By having the browser to list
an abstract method (and a final, non-extensible one), the framework designer
communicates quite clearly that there is a slot that must be filled by the
extension (and one which has been filled already is not to be touched
anymore). Compare this to situations with implemented but extensible
procedures. I never know what to do, overwrite it, and if I do, to call super
or not to call super? I am now talking of the more general pattern, not the
relatively well-known externalize / internalize situation.

All the best,

Wolfgang Weck
Received on Mon Feb 15 1999 - 21:28:54 UTC

This archive was generated by hypermail 2.3.0 : Thu Sep 26 2013 - 06:27:42 UTC