Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!usc!snorkelwacker!spdcc!merk!alliant!linus!think!barmar From: barmar@think.com (Barry Margolin) Newsgroups: comp.object Subject: Re: Should Shapes Display Themselves? Message-ID: <41620@think.Think.COM> Date: 17 Aug 90 06:25:07 GMT References: <10885@crdgw1.crd.ge.com> Sender: news@Think.COM Organization: Thinking Machines Corporation, Cambridge MA, USA Lines: 118 In article 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