{Begin SubSec Combining Inherited Methods} {Title Combining Inherited Methods} {Text {Tag SuperClasses} {note need here a complete explaination of Inheritence, including precedence order of supers --- mjsann} In practice, most methods used to manipulate LOOPS objects are inherited. In the simplest examples of multiple inheritance, classes represent independent features and there is no conflict between inherited methods. However, when features inherited from classes interact, it is essential to be able to describe how to combine them. Howard Cannon recognized this "mixing issue" as central in the design of Flavors: {Begin LabeledList} {Indent 7percent} {Text "To restate the fundamental problem: there are several separate (orthogonal) {it attributes} that an object wants to have; various {it facets} of behavior (features) that want to be independently specified for an object. For example, a window has a certain behavior as a rectangular area on a bit-mapped display. It also has its behavior as a labeled thing, and as a bordered thing. Each of these three behaviors is different, wants to be specified independently for each object, and is {it essentially} orthogonal to the others. It is this "essentially" that causes the trouble." "It is very easy to combine completely non-interacting behaviors. Each would have its own set of messages, its own instance variables, and would never need to know about other objects with which it would be combined. Either the multiple object or simple multiple superclass scheme could handle this perfectly. The problem arises when it is necessary to have {it modular} interactions between the orthogonal issues. Though the label does not interact {it strongly} with either the window or the border, it does have some minor interactions. For example it wants to get redrawn when the window gets refreshed. Handling these sorts of interactions is the Flavor system's main goal." ... from [Cannon82] }{End LabeledList} This section considers cases where the inherited features interact, and describes some LOOPS facilities for combining interacting methods. First, we describe a way of combining an inherited method with local method code. Next, we describe other ways of combining methods inherited from multiple super classes. Finally, we describe some special functions one can use to "escape" from the normal method inheritence conventions. {Begin SubSec Augmenting an Inherited Method} {Title Augmenting an Inherited Method} {Text The inheritance examples shown previously considered only cases where methods are inherited in toto. In these examples, subclasses inherit a method or value unchanged, or they override it completely. No mechanism was described that would enable a subclass to track{note ?} changes in a method after it had been specialized in some way. For combining an inherited method with local code, LOOPS provides the special method invocation {fn _Super}. {FnDef {Name _Super} {Args object selector arg{sub 1} {ellipsis} arg{sub N}} {Type NLAMBDA NOSPREAD} {Text {arg object} is the object to which the method is applied (typically {lisp self}), {arg selector} is the selector for the method and {arg arg{sub 1}} {ellipsis} {arg arg{sub N}} are the arguments for the method. As with {fn _}, {arg selector} is not evaluated; the remaining arguments are evaluated. {fn _Super} provides a form of relative addressing; it invokes the next more general method of the same name even when the specialized method invoking {fn _Super} is inherited over a distance.{note ?} An example of the use of {fn _Super} is given in {FigureRef BorderedWindow.Refresh}. Note: {fn SENDSUPER}{index SENDSUPER Mac} can be used instead of {fn _Super}. }} {Begin Figure} {Tag BorderedWindow.Refresh} {Text {lispcode (BorderedWindow.Refresh [LAMBDA (self) (* mjs: "11-JAN-82 19:28") (* * Method for refreshing a window that has a border) (* First use the refresh method inherited from Window.) (_Super self Refresh) (* Then Re-display the border.) (_ (@ :border) Display) self])} } {Caption This Interlisp procedure implements the {lisp Refresh} message for the class {lisp BorderedWindow}. It uses {fn _Super} to invoke the more general method in the class {lisp Window}. The object for the "border" of the bordered window is in the instance variable {lisp border}. The specialized method returns the bordered window as its value. In more complicated examples, calls to {fn _Super} and {fn _} can be combined using Interlisp iterative and conditional statements. } {End Figure} }{End SubSec Augmenting an Inherited Method} {Begin SubSec Combining Multiple Inherited Methods} {Title Combining Multiple Inherited Methods} {Text Using {fn _Super}, a method can invoke the {it single} next general method. However, when a class has multiple super classes, sometimes it is necessary to invoke the general methods from {it each} of the super classes.{note example!!} In this situation, one can call {fn _SuperFringe}: {FnDef {Name _SuperFringe} {Args object selector arg{sub 1} {ellipsis} arg{sub N}} {Type NLAMBDA NOSPREAD} {Text This is similar to {fn _Super}, except that {fn _SuperFringe} invokes the next more general method of the same name for {it each} of the super classes on the supers list of the class of the currently-executing method. }} }{End SubSec Combining Multiple Inherited Methods} {Begin SubSec General Method Invocation} {Title General Method Invocation} {Text The functions {fn _Super} and {fn _SuperFringe} have proved to be sufficient for implementing most methods. However, sometimes it is necessary to manipulate multiple inherited methods, and invoke them in some other order. The following functions provide more general ways of invoking particular methods. It is important to note that while these functions are more powerful than {fn _Super} or {fn _SuperFringe}, they are also more "dangerous", in that they do not conform to the conventions of method inheritence. These functions should only be used as a last resort when a method cannot be implemented in any other way. {FnDef {Name DoMethod} {Args object selectorExpr class arg{sub 1} {ellipsis} arg{sub N}} {Type NLAMBDA NOSPREAD} {Text {fn DoMethod} allows computation of the name of the selector and the class from which that method should be found; it applies that method to {arg object}. All the arguments to {fn DoMethod} are evaluated; {arg selectorExpr} should evaluate to a selector name in the class computed from {arg class}. If {arg class} is {lisp NIL}, then the class of {arg object} is used. If no method for the computed selector is found in the computed class, an error is generated. The remaining arguments, {arg arg{sub 1}} {ellipsis} {arg arg{sub N}} are the arguments for the method. }} In the case where the arguments to the method have already been evaluated, then one can use {fn ApplyMethod} instead of {fn DoMethod}: {FnDef {Name ApplyMethod} {Args object selector argList class} {Text {arg argList} is a list of all the arguments to the method (except {arg object}) already evaluated. The function applied is the one found by searching from {arg class}. If {arg class} is {lisp NIL}, the class of {arg object} is used. }} {FnDef {Name DoFringeMethods} {Args object selectorExpr arg{sub 1} {ellipsis} arg{sub N}} {Type NLAMBDA NOSPREAD} {Text Like {fn DoMethod}, all of the arguments are evaluated. {fn DoFringeMethods} calls the method for {arg selectorExpr} in the class of {arg object}, if that method is defined in that class. If the method is not defined in the class of {arg object}, the method of the same name for {it each} of the super classes on the supers list of the class of {arg object} is envoked. }} }{End SubSec General Method Invocation} }{End SubSec Combining Inherited Methods}