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