On Sep 23, 2004, at 10:07 AM, Brian Candler wrote: > Each object individually can have methods added and removed > dynamically, so > what's important is whether a particular object X has method Y right > now, > rather than the class it came from. I agree with a lot of what you say in general, Brian. Although with the above, I don't suspect that there will be any code extending specific instances on the fly; it simply wouldn't occur in the Plan that's in my head. While I'm normally a huge duck-typing-plus fan, I'm waffling in this case. I think the reason is that I hope to have adaptor modules (classes) being contributed from many different authors, providing me with two challenges: 1) Given the large number of devices which fit under 'automation', I want a hierarchical organization of devices which may be available. If I leave it up to each author, I fear the user will install an adaptor for their hardware and find that it's under LightSwitch/Dimmer while every other similar adaptor is in LightSwitches/Adaptors. Or worse, it's in DimmerSwitches, or Electrical/Wall/Analog/Slider. So, I feel the need to impose my own template hierarchy (at least as a starting framework) for displaying the adaptors. While I could use a hierarchical storage of symbols, I like the idea of using a class namespace hierarchy. It gives me hierarchical classification of an adaptor, and at the same time gives me a modicum of protection from adaptor authors running over each others' code (through namespace collisions). 2) Similar to the above, the more leeway I give the authors in the method naming convention, the more I fear that some switches will have "Turn On", some will have "On", and some smartass authors will use "Illuminate".Having thought about your points, I will certainly need to look at the exposed methods for any adaptor and be able to use them. (Who knows what bizarre functionality I will forget to include or account for?) But, for the sake of consistency, I think I want to say "Damnit, if you have a 'Light' class, you better have a :turn_on method." And again, these methods should be hierarchically organized, conveniently corresponding to the device hierarchy already being defined to solve the classification and namespace problem. > Just take a pool of objects representing the things you want to > control and > present it to the user. If you want to have the GUI dynamically adjust > itself for each object, then you can check if the method exists for > each > object before you present it: > > gui.add_button("Turn On", foo, :turn_on) if > foo.respond_to?(:turn_on) > gui.add_button("Turn Off", foo, :turn_off) if > foo.respond_to?(:turn_off) > > (or the objects themselves could contain code which knows how to > create the > appropriate GUI controls, but personally I prefer to keep knowledge > about > presentation in the GUI. After all, you could have many different > types of > interface controlling the same set of objects: web, command-line, > Tcl/Tk, > etc) Just to clarify my earlier point - you're right, the adaptors shouldn't know how to make the GUI, but the GUI does need to know how to describe the interface. What you list above is likely equivalent to what I'll end up doing (ensuring that the method exists). However, since I can't know all the methods/labels which might be needed, I'll likely be ensuring that it exists by iterating the exposed methods, and at that point I'm at the naming mercy of the author. And I don't want to be at their mercy for certain core functionality. > However, in this case, it may be better to return a data structure > designed > for the GUI to prompt the information. This can include a > human-readable > description, a validation regexp, and perhaps a method to call to > convert it > from a string into the desired format. Aye, I'm thinking even further (for rich GUI), along the lines of: class Foo::Bar::Whee::La < Foo::Bar::Whee def turn_on; ...; end def get_funky( speed, dance_style, song ); ...; end ... describe_method( :turn_on, "Turn On", "Turns on the switch" ) describe_method( :get_funky, "Get Funky", "Breaks down to the beat.", { :speed => { :type => :integer, :min => 0, :max =>100 }, :dance_style => { :type => :list, :values => ['Hip Hop','Disco' ] }, :song => { :type => :file, :mask => /\.(mp3|ogg|aiff)$/ } } ) end > If you have a common set of methods which are shared by many objects, > consider putting them into a Module which can then be included in a > class: Well, as Robert correctly surmised, I don't actually want to implement common code to them. (Some adaptor authors may want to, and they're welcome to define their own modules if they wish). What I'm probably going to end up with is something like: class Device def self.mandatory_methods= ( *method_names ) @mandatory = method_names.flatten.to_set @mandatory.merge( superclass.mandatory_methods ) if superclass.respond_to? :mandatory_methods end def self.mandatory_methods @mandatory end self.mandatory_methods = [:name, :manufacturer, :models] class Light < self self.mandatory_methods = [:turn_on, :turn_off] class Dimmer < self self.mandatory_methods = [:level=, :level] end end class Outlet < self self.mandatory_methods = [:turn_on, :turn_off] end end along with a test for these methods as part of my #instance handling. > Finally, as a general point, do consider delegation rather than > inheritance; > that is, object X "has_a" Y, rather than object X "is_a" Y. It often > turns > out to be a far more flexible way to make objects with composite or > more > 'intelligent' behaviour than the base object. Good advice; I'll certainly think about some nice way to group primitives in the user UI. Thanks so much for your considered advice. -- (-, /\ \/ / /\/