On Wed, 3 Sep 2003 09:33:02 +0900 David Naseby <david.naseby / eonesolutions.com.au> wrote: <snip> > I like this style too, and Ruby allows one to do it well via Mixin Oriented > Programming (MOP?). The best library based example I've seen is in Amrita, > where you extend your model class with Amrita::ExpandByMember, so amrita can > render the object correctly. > > Similarly, you don't need to expand classes to predict there uses - for the > example of IO printing flying around, you can extend on the fly: > module STDOUTPrinter > def print > STDOUT.print self.to_str > end > end > > str = "helloworld" > str.extend STDOUTPrinter > str.print > > Its a fairly useless example in this case, but extend this to template based > GUIs along the amrita lines, and mixin oriented programming shows its real > strength. <snip> Actually this isn't a very good example. This whole argument about "print should be a member of an object" is demonstrably inelegant and wrong, for a number of reasons (some of which have been noted already): * The action of "printing" presupposes a few things: - A device to print on - A format in which to print - Content to print * A String specifies no device on which to print * A String provides content and, to some extent, format * The physical act of printing is device-dependent, and thus in OOP should be encaspulated within the device representation object * While it is possible, as in the above example, to provide a method which calls on a device to print, this couples String with the concept of printing, which may not be meaningful in all contexts. * This does not scale well, as it requires N methods for N classes, as opposed to one method for N classes. There's an important OOP concept that's somewhat unspoken, but should be heeded rigorously: containership. This means that if you look at a given thing, actions or attributes that are not contained _within_ that thing should _not_ be a part of that thing. Following this principle avoids two problems: * Confusion: When telling an object to do something, you can be sure it's the object doing it, and not another object doing the action on "self", as in a previous example in this thread: foo.eat(person) # The food is eating the person? person.eat(food) # No, the person eats the food food.eaten_by(person) # Good reaction method, also clear; # should be called by Person#eat * Undesired coupling: If you store outside data or define outside actions, you will end up in a situation where your methods require outside context which isn't there: w = World.new sp = Sphere.new(w, 0, 0, 5) # Bad coupling, the location and # world should not matter here : # elsewhere sp = Sphere.new(??.., 5) # We might not have a World here, # we just want a sphere sp.circumference # Outside information not relavent # to the sphere itself # Someone else implements it like this: w = World.new sp = Sphere.new(5) w.add(sp, 0, 0) # Good, no coupling : sp = Sphere.new(5) # No ties to worry about sp.circumference These get even worse as you start trying to tie objects and methods with less relation together, like String and IO. Would you consider a String#display function that takes a GraphicsDevice? A GD and coordinates? colors? fonts? transformations? Things get out of hand quickly. What if you're working with multiple interfaces? String#print is an identical concept, except it's merely displaying on an inferior rendering device. And it won't work for ncurses, or without a console. Could you use mixin to abstract #print to different devices? Yes. Could you abstract the formatting complexities? Maybe. Can you ensure the concept of printing even makes sense at the target? No. Thus, tying these sorts of things is not elegant, and not a good idea. Now, if you want a good example of mixins, think of creating a class which is multifaceted---but beware of violating containership, and of the distinction between is-A and has-A. For instance, if you were constructing a SpaceVessel, the following would be good: class MyVessel < SpaceVessel include Battleship include Cargoship end The following would not, of course: class MyVessel < SpaceVessel include SuperLuminalEngines include Weapons end Anyhow, this message is getting far too long, so that's all. -- Ryan Pavlik <rpav / mephle.com> "Let me tell you, I find your math to be highly suspect." - 8BT