weeks@hpdtl.HP.COM (Gregory Weeks) (09/09/88)
I'm aware that the message-sending paradigm of Smalltalk doesn't smoothly handle all situations. There is one situation in particular that I'm curious about. Suppose I have a useful procedure in mind that accesses its argument only by sending it certain messages. Suppose further that I want to be able to apply this procedure to any object that recognizes these messages. There are many ways to implement such a procedure. 1. For every target class [that is, for every class that recognizes the messages used in the procedure], define a method that implements the procedure. 2. Arrange somehow for all of the target classes to have a common superclass and implement the procedure as a superclass method. 3. Define a block that implements the procedure and bind it to a global variable. 4. Implement the procedure as a method of a nontarget class. Are some of the above choices clearly superior? Is there an established Smalltalk style that rules out some of the choices?
hibbert@arisia.Xerox.COM (Chris Hibbert) (09/13/88)
> weeks@hpdtl.HP.COM (Gregory Weeks) > Suppose I have a useful procedure in mind that accesses its argument only > by sending it certain messages. Suppose further that I want to be able to > apply this procedure to any object that recognizes these messages. There > are many ways to implement such a procedure. > > Are some of the choices clearly superior? Is there an established > Smalltalk style that rules out some of the choices? Yes there are better and worse choices, and yes there is an established Smalltalk style. The better choices and Smalltalk style don't always coincide. The options you suggest in rough best-to-worst order: > Arrange somehow for all of the target classes to have a common > superclass and implement the procedure as a superclass method. This is the best choice when it's possible. If you're writing code from scratch, you want to try very hard to arrange that classes that should support the same protocol share a superclass. This often means creating an "abstract" superclass that isn't designed to have any instances itself, and instead acts as a template for its subclasses. When you're adding code to existing classes, or when you can't find a way to arrange the class hierarchy to satisfy all your objectives, you have to choose from the other mechanisms. > Implement the procedure as a method of a nontarget class. Here's where "clearly superior" and Smalltalk style diverge. (That evaluation may be controversial.) Smalltalk style says you should put this kind of functionality in a class unrelated to the behavior like class SystemDictionary. Lots of cruft has found its way into class SystemDictionary over the years. A better solution might have been to put this kind of behavior in a newly created nontarget class. For instance, all the methods in the system that deal with memory space could have been put in a MemoryManager object instead. If the interface was well designed, it might be possible to reify the interface and provide a real MemoryManager object in the interpreter. Instead, what we have looks like clutter. > 1. For every target class [that is, for every class that recognizes the > messages used in the procedure], define a method that implements the > procedure. This solution would be a maintenance nightmare. When (not if) the generic procedure needed to be changed, all of the copies would have to be found and changed. If some copies of the method had been stored in files, they wouldn't be found by any change management mechanism I've seen implemented for Smalltalk. (Which is not to say that someone couldn't write such a mechanism.) > 3. Define a block that implements the procedure and bind it to a global > variable. This is not recommended at all. If you do this, then no one will ever be able to find your code. (No one ever looks for blocks in the Global dictionary, and none of the tools in the system are designed to expect that.) In addition, finding the source text for a compiled block is only easy when you're using the browser. There are other ways of dealing with this, but unfortunately, none of them match Smalltalk style. Another possibility is multiple inheritance. Several varieties of LISP support multiple inheritance, in particular ZetaLISP has "flavors", which allows you to "mix-in" a little bit of behavior from one superclass to several subclasses which don't need to share storage format. Chris