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.

--
(-, /\ \/ / /\/