Re: about genricity

From: Erik Ostrom <eostrom_at_research.att.com>
Date: Wed, 11 Sep 1996 15:08:16 -0400

> I am writing a software based on classes that write and read themselves
>on files.

For the moment, I'm going to assume that you mean that _instances_ of the
classes are read and written on files....

> When I want to read a class, I don't know how to do it in a clean way.
> Here is what I do
>
> (while not-eof-port
> read line on port
> if line match a regexp corresponding to child-class-1
> the create an instance of child-class-1
> (read child-class-1 port)
> if line match a regexp corresponding to child-class-2
> the create an instance of child-class-2
> (read child-class-2 port)
> ...
>
> Each time I create another heir of parent-class I have to modify this
> code. I find that this sequence of "if" is against the spirit of
> polymorphism but I can't manage to find another way to do it. Is there a
> classic way to handle such problems ?

As far as I know, you can't really eliminate the essential if-ness of
the solution, but you can bury it down under lots of code. There are
any number of variations on what I'm about to suggest, but the basic
idea is: Make the regexp associated with each class an aspect of the
class itself, and keep a list of classes that can be read and written
in this manner. Then, when you need to read something, just go
through the list checking regexps.

Detailed description follows; feel free to skip it....

------------------------------------------------------------
First, declare a variable for the list of classes (actually a
list of "representative instances" of each class, since it's
hard to operate on classes directly):

  (defvar *list-of-classes* '())

Next, define a generic function that returns the regexp associated
with the class of a given instance. Each subclass will define its
own method for this function.

  (define-generic read-ok-regexp)

Now, each time you define a subclass, you need to (a) define
its read-ok-regexp method, and (b) register an instance of it
in *list-of-classes*. Like so:

  (define-class child-class-1 (parent-class)
    (...))
  (define-method read-ok-regexp ((instance child-class-1)) "^child-class-1$")
  (set! *list-of-classes* (cons (make child-class-1) *list-of-classes*))

Define a method on parent-class that tests if a given line
matches its regexp; if so, creates a new instance and reads
it in; and if not, returns false. This method only has to
be defined once, on the parent class.

  (define-method maybe-read ((instance parent-class) line port)
    (if ((read-ok-regexp instance) line)
        (read (make (class-of instance)) port)
        #f))

And, finally, your general-purpose input code looks like this:

    read a line on port
    (let loop ((classes *list-of-classes*))
      (if (null? classes)
        #f
        (or (maybe-read (car classes)) ;; if we read it, return it;
            (loop (cdr classes))))) ;; otherwise, keep iterating

This code returns an instance of a class if it could be read, or #f
if no classes matched the line.
------------------------------------------------------------

Now, this is still pretty ugly--you have to define an extra method
and set *list-of-classes* each time you create a readable/writable
class. (Actually, I would use a metaclass to cover up some of the
mechanics of this, but that's just me.) But at least the aspects
of reading that are specific to each class (the regexp to start
with, and the read method) are _defined with_ the class,
and you only have to write the general-purpose input routine
once.
Received on Wed Sep 11 1996 - 21:26:02 CEST

This archive was generated by hypermail 2.3.0 : Mon Jul 21 2014 - 19:38:59 CEST