[comp.object] Should Shapes Display Themselves?

halvers@altair.crd.ge.com (Pete Halverson) (08/09/90)

In article <CLINE.90Aug8130349@cheetah.ece.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:

>QUESTION: Should a `Shape' be able to `draw' itself?
>
>The usual answer is ABSOLUTELY....However: what if I add a new terminal
>type?  Or what if I want a textual `view' (using MVC terminology) rather
>than a graphical `view'?

CLOS explicitly deals with this kind of "orthogonal polymorphism" in the
form of multimethods, in which behavior can be dependent on any or all of
the arguments, not just the first.   Thus, one can define a method for
each combination of shapes and view types (at whatever level of abstraction
is necessary). For instance, given a class structure like 

    (defclass shape ()
        ()
      (:documentation "base shape class"))

    (defclass square (shape))
    (defclass circle (shape))
    (defclass hexagon (shape))

    (defclass view ()
        ()
      (:documentation "base view class"))

    (defclass text-view (view))
    (defclass graph-view (view))

you could define multimethods like

    (defmethod draw ((shape square) (view text-view))
      ;; print "I'm a square"
    )

    (defmethod draw ((shape square) (view graph-view))
      ;; render square
    )

   ...etc.
          
--
===============================================================================
Pete Halverson                        		    INET: halverson@crd.ge.com 
GE Corporate R&D Center	                      UUCP: uunet!crd.ge.com!halverson
Schenectady, NY                     "Money for nuthin' and your MIPS for free"

cline@cheetah.ece.clarkson.edu (Marshall Cline) (08/09/90)

In article <10885@crdgw1.crd.ge.com> halvers@altair.crd.ge.com (Pete Halverson) writes:
>In article <CLINE.90Aug8130349@cheetah.ece.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>>QUESTION: Should a `Shape' be able to `draw' itself?
>>The usual answer is ABSOLUTELY....However: what if I add a new terminal
>>type?  Or what if I want a textual `view' (using MVC terminology) rather
>>than a graphical `view'?

>CLOS explicitly deals with this kind of "orthogonal polymorphism" in the
>form of multimethods, in which behavior can be dependent on any or all of
>the arguments, not just the first. [...]
>    (defmethod draw ((shape square) (view text-view))
>      ;; print "I'm a square"
>    )
>    (defmethod draw ((shape square) (view graph-view))
>      ;; render square
>    )

Ok, you can get this sort of thing in C++ with a bit of code bloat by
top-level functions and exact matches on both parameters [however the
CLOS scheme is more general in general :-) since it has multiple
polymorphism where C++ uses a `compile-time-best-match' scheme].
Furthermore the top-level function can be a `friend' of `Square' so
it can get at the Square's "I am a Square" name and/or vertices etc.

But this brings us back to ``operating on objects'' rather than
empowering objects to do things for us.

Would double dispatching allow this to be expressed better?

Marshall Cline

--
==============================================================================
Marshall Cline / Asst.Prof / ECE Dept / Clarkson Univ / Potsdam, NY 13676
cline@sun.soe.clarkson.edu / Bitnet:BH0W@CLUTX / uunet!clutx.clarkson.edu!bh0w
Voice: 315-268-3868 / Secretary: 315-268-6511 / FAX: 315-268-7600
Career search in progress; ECE faculty; research oriented; will send vita.
PS: If your company is interested in on-site C++/OOD training, drop me a line!
==============================================================================

sjs@roland.ctt.bellcore.com (Stan Switzer) (08/10/90)

> >>QUESTION: Should a `Shape' be able to `draw' itself?
> >>The usual answer is ABSOLUTELY....However: what if I add a new terminal
> >>type?  Or what if I want a textual `view' (using MVC terminology) rather
> >>than a graphical `view'?

Well, OK then.  A shape should be able to display itself.  Trouble
is, unless you are implementing a drawing editor (aren't there enough
already?) you aren't likely to come across an actual "shape" object.
You are more far likely to come across an object like
"Set_Of_Automobile_Parts".  It will probably be necessary to display
this object.  I propose that it makes a lot of sense to define
technology-dependent classes like "Sequence_Displayer" which can
display sequences of things (w/ scrolling, etc.)  It really doesn't
make sense to put display logic in the sequence class (except,
*maybe*, a textual display method for debugging purposes).

This is a common error in graphical interface systems.  People define
menu classes as a subclass of "Display_Region" when it should be a
subclass of "Sequence" or "Dictionary."  Stepstone's ICPak 201 got
this right, though it took me quite a while to see it.

Anyway, when I originally proposed this thread, I said:

> Leaving aside objects which are explicitly intended as display
> artifacts (windows, menus, etc.) and considering application domain
> objects such as personnel files, tractors, and fishing boats, should
> these objects be concerned with how they are displayed or should we
> separate the objects (models) from views?

I've received a number of interesting email responses so far.  I'll
summarize in a subsequent note.

Also, does somebody have any good references on the
Model-View-Controller paradigm?  There seems to be a dearth of
published information.

Thanks,

Stan Switzer  sjs@bellcore.com

dean@image.soe.clarkson.edu (Dean Swan) (08/13/90)

>> Leaving aside objects which are explicitly intended as display
>> artifacts (windows, menus, etc.) and considering application domain
>> objects such as personnel files, tractors, and fishing boats, should
>> these objects be concerned with how they are displayed or should we
>> separate the objects (models) from views?

What I've seen here so far is this:

   1) Mechanism *should* be separated from policy.
   2) Objects *should* be able to display themselves.
   3) Having separte 'view' objects would destroy the encapsulation
      provided by an object.

Statement #1 is true.  Statements #2 and #3 are false.
The advantages of separating mechanism from policy have been expounded upon
enough, so I won't reiterate what others have already stated.

As to Objects being able to display themselves:  The major reason for an
object to be able to display it self is for performance reasons.  Other than
that, this is a big violation of the mechanism/policy separtation rule.

Think about how we visually percieve things in the real world.  Light (which
for purposes of this discussion can be considered and object) emanates from
a source, travels some path, reflecting off an object, and into our eyes.
The light has characteristics (intensity, color, coherency, velocity (a
vector quantity), etc.) so it can reasonably modeled as an object, and of
course a page in a book, or whatever we are viewing can also be modeled as
an object.  A light source could send an illumination message to an object,
and the object could send a reflection message to the camera, eye, etc.

Of course this gets horribly complex, very quickly, and we don't need all
of the functionality that the real world model provides most of the time,
so we start to make simplifications/optimiztions for the limited set of
circumstance that we require.

In a previous example, somebody mentioned a 3D graphics system, and the
question was raised "How should an object know if it is obscured by
another object?"  It shouldn't!  How do we know that atoms have dense nuclei,
and orbiting electrons?  Because Rutherford was able to shoot aplha particles
"through the holes" in a piece of gold foil, but even this is only an
educated guess with enough supporting evidence that we accept it as fact.

Someone else suggested using an intermediate display language to implement
'drawMe'.  This is a step along the right vector, but in the wrong direction.
Objects should be able to respond to a set of messages that provide a 'view'
object enough information to render the object.

The problem with this idea is that there are too many possibilities to be
able to practically design a standard set of messages that all objects can
respond to, to make them 'view-able' in any context that a user could wish
for.

So that leads us back to 'Yes, ABSOLUTELY, objects should be able to
display themselves.' and it comes full circle.

I would put forth the idea that the major reason that mechanism and policy
do not remain strictly separated is that performance considerations frequently
outweigh the advantages of separating mechanism and policy.

A good comparison is separation of church and state.  The rule is that they
must remain strictly separate, but there's another rule that says people must
be free to practice their religion.  Well, should children be allowed to pray
in schools funded by the state?  According to the first rule, no they should
not.  According to the second rule, yes they should.  And the real world
solution is to allow it to be dealt with independantly for each 'instance'.
(And by using the word 'instance', I do mean to imply the concept of
 instantiation - the result of: SomeClass new.)

Ok, I've babbled long enough.  Am I making any sense?

-----------------------------------------------------------------------------
Dean Swan
dean@sun.soe.clarkson.edu
-----------------------------------------------------------------------------

barmar@think.com (Barry Margolin) (08/17/90)

In article <CLINE.90Aug9123455@cheetah.ece.clarkson.edu> cline@sun.soe.clarkson.edu (Marshall Cline) writes:
>In article <10885@crdgw1.crd.ge.com> halvers@altair.crd.ge.com (Pete Halverson) writes:
>>CLOS explicitly deals with this kind of "orthogonal polymorphism" in the
>>form of multimethods
>But this brings us back to ``operating on objects'' rather than
>empowering objects to do things for us.
>Would double dispatching allow this to be expressed better?

I haven't quite figured out how to employ it for this problem, but my
suspicion is that method combination and multimethod dispatching together
may supply the answer.

CLOS method combination allows methods from different classes to be
combined automatically into the method that is actually run when the
generic function is invoked.  With :AROUND methods, the method that is run
first can manipulate the parameters and dynamic state before passing
control to the next method.  And :BEFORE methods can manipulate dynamic
state.

For this particular example, I suspect such a complex solution isn't
necessary.  Object-oriented design involves determining what each object
"knows".  In the case of displaying shapes in views, the shapes know their
name and abstract image, but not how to render either of these things on
a physical device, while views know how to render text and/or graphics, but
not what any particular shape looks like.

The solution is for one or the other object (and I don't think it matters
which) to ask the other for the information it needs to complete the
operation.  If the shape object is given control, it can ask the view
"should I invoke a graphical or textual output routine?", and depending on
the answer it will invoke appropriate rendering functions of the view.
Alternatively, the view object can ask the shape object for its textual
name (if the view is textual) or a drawing procedure (if the view is
graphic) and then do the right thing; the drawing procedure would receive
the view as an argument and perform the appropriate graphics operations on
the view.

If the answer to the question is represented in the type hierarchy, then
the query can be automated using the generic dispatch mechanism.  The first
style above would look (in CLOS) like:

(defmethod draw (shape (view textual-view))
  (draw-textually shape view))

(defmethod draw (shape (view graphical-view))
  (draw-graphically shape view))

(defmethod draw-textually ((shape triangle) view)
  (draw-textually "Triangle" view))

(defmethod draw-textually ((shape square) view)
  (draw-textually "Square" view))

(defmethod draw-textually ((s string) (term dumb-terminal-view))
  (write-string s (output-stream term)))

(defmethod draw-textually ((s string) (w x-window-view))
  ;; I'm just making up a possible CLX function name
  (xlib:draw-string (window-id w) s))

(defmethod draw-graphically ((shape polygon) view)
  (do* ((vertices (shape-vertices shape) (cdr vertices))
	(vertex-1 (first vertices) vertex-2)
	(vertex-2 (second vertices) (second vertices)))
       ((null (cdr vertices))
	;; Draw closing line
	(draw-graphically (make-line vertex-1 (first vertices)) view))
    (draw-graphically (make-line vertex-1 vertex-2) view)))

(defmethod draw-graphically ((l line) (w x-window-view))
  (xlib:draw-line (window-id w) (line-vertex-1 l) (line-vertex-2 l)))

Yes, at some point this degenerates into operating on the objects rather
than having the objects "do" things.  In a system with multimethods we tend
to think less about objects "doing" things, because the methods aren't
attached to single classes -- there is no inherent "self" or "this".

The second style that I described above would look something like this:

(defmethod draw (shape (view textual-view))
  (funcall (shape-text-displayer shape) view))

(defmethod draw (shape (view graphical-view))
  (funcall (shape-graphic-displayer shape) view))

(defmethod shape-text-displayer ((shape triangle))
  #'(lambda (view) (draw-textually "Triangle" view)))

(defmethod shape-text-displayer ((shape square))
  #'(lambda (view) (draw-textually "Square" view)))

(defmethod shape-graphic-displayer ((shape polygon))
  #'(lambda (view)
      (do* ((vertices (shape-vertices shape) (cdr vertices))
	    (vertex-1 (first vertices) vertex-2)
	    (vertex-2 (second vertices) (second vertices)))
	   ((null (cdr vertices))
	    ;; Draw closing line
	    (draw-graphically (make-line vertex-1 (first vertices)) view))
	(draw-graphically (make-line vertex-1 vertex-2) view))))

In this case, the objects are "doing" things but what they are doing is
answering questions about themselves.  You ask objects, "how do I display
you textually/graphically?" and they provide directions.

Note how this mirrors the real world.  Consider how you get to someone's
house for a party.  The person might have included directions with the
invitation, and the directions might say "If you're driving from the west,
....  If you're coming by bus, ....  Etc."  Or he might have said, "call
for directions", and then you'd call and ask "how do I get there by bus?"
Once you have this information you then have to use your own knowledge of
how to drive a car or get on a bus and apply the appropriate directions to
that mode of travel.
--
Barry Margolin, Thinking Machines Corp.

barmar@think.com
{uunet,harvard}!think!barmar