[Prev][Next][Index][Thread]
Dylan, MOP, object/relational mapping...
-
Subject: Dylan, MOP, object/relational mapping...
-
From: Scott Ribe <sribe@miqs.com>
-
Date: Wed, 10 Nov 1999 16:45:05 -0500 (EST)
-
Xref: grapevine.lcs.mit.edu comp.lang.dylan:11057
I'm trying to learn to think in Dylan terms, and need a sanity check
here.
Background:
First, a description of a design from which I would like to lift certain
features. (I'm no expert in this; my explanation may be a little weak in
the details.)
In EOF (Enterprise Objects Framework) from Apple, there is an
EOGenericRecord class that represents "business objects" that are mapped
into a relational database. This class uses features of Objective-C to
allow access of attributes (columns) to be uniformly presented to
clients whether the access is done through a direct instance of
EOGenericRecord or a subclass that implements custom accessors.
And it does that without messy code generation. In general, what I'm
trying to accomplish here is often accomplished by generating the code
of skeleton or base classes to represent the database entities. I
consider this an undesirable approach for many reasons. EOF and
Objective-C provide a solution that I consider elegant, and I'd like to
design an elegant solution in Dylan.
The EOGenericRecord simply stores the attributes (columns) pulled from
the database in a dictionary of name/value pairs. There is a function
(getValueForKey or some such) on this class that takes as argument the
name of an attribute, looks it up in the dictionary, and returns the
value. But this function is not normally called by clients. For an
attribute named "foo" the client calls the method "getFoo", the method
is not found in the class, the call is forwarded to the class's
"delegate", which has access to information about the call including the
function name (ok, really a message selector in Objective-C), and the
delegate then calls getValueForKey ("foo"). The function setValueForKey
works similarly for setting attributes.
So the result is that a client calls getFoo and setFoo on an
EOGenericRecord, and if the implementation changes so that this
particular entity becomes a subclass of EOGenericRecord that implements
custom accessor functions getFoo and setFoo, no client changes are
required. This is quite a nice feature, that entities which are not
differentiated with custom behavior automatically act as though they
have accessor functions for each attribute. Then as the model evolves
and entities are differentiated and are given custom accessors, clients
require no changes.
Now I don't think there is any way in Dylan to directly emulate this
design. If the standard specified more information made available for an
umimplemented method failure then perhaps that information could be used
in a handler that would pick up the method name and redirect it to a
dictionary lookup, just like EOF. Maybe. But even if that were possible,
it feels wrong for Dylan. In Objective-C the notion of a delegate is
integral to the dynamism of the language and used in many places.
Abusing Dylan's error-handling to imitate delegation has sort of a nasty
feel to it. (This coming from someone who has written all of a couple of
pages of Dylan code and not designed anything in Dylan. Correct me if
I'm off track.)
It does appear that an alternative is possible. Though the reading I've
done (all of Dylan Programming and a few pages here and there of DRM)
does not emphasize this, it seems to me that the first-class nature of
classes and methods in Dylan form at least a limited meta-object
protocol that could be used to get the same feature, albeit from a very
different design.
Assume:
There is an instantiable base class (let's call it <base-entity>) to
serve as the simplest kind of entity mapped from a relational database,
and that it stores its attributes in a dictionary of name/value pairs.
Many entities from the database will simply be mapped to this class, but
some will be mapped to derived classes with custom behaviors, including
custom accessor methods. There is a data dictionary that is read at
startup which describes the mapping from database tables/entities into
<base-entity> and its derived classes.
Hypothesis:
When reading the data dictionary, for each entity, it would possible to
find the <class> object corresponding to the subclass of <base-entity>
to which the entity should be mapped (located by name), then for each
attribute of the entity check to see if there is a getter method, and if
there is not then create one.
Of course this would need to be done for setters as well as getters.
Read and write access privileges would be supported in the data
dictionary and handled appropriately in the code. When I talk about
looking for a method and/or creating one, I know I'm really talking
about looking for a method within a generic function, and creating the
generic function if needed.
I expect that "creating the getter method" would happen as follows: The
dictionary of attributes would actually be implemented as an array of
name/value pairs (or a pair of arrays, or any other collection that
allows for direct indexed access); there would be a method on
<base-entity> getIndexForKey that would return the index of an attribute
with the given name; there would be a method getValueForIndex that would
take an attribute index and return the value. Then creating a setter for
a specified attribute would consist of using getIndexForKey on the
attribute name, putting the result into a local variable, and returning
a curried call to getValueForIndex.
Question:
Could it be done this way? Any comments?
Follow-Ups: