A comment by Thomas Geis on my first RubyBeans post lead me to another rework of the RubyBeans metaprogramming example.

Thomas accurately points out:

I think the property should sit in a proxy-object, that does inform the listeners of its change. In your example, one could directly change the properties value from within the class without a call to the property_changed method. This would be a concern for me. Isn’t it a concern for you?

Yes it is! I tried the proxy-object solution, but I am not satisfied with it for two reasons: the code get lengthy and inelegant, but mostly because the proxy-objects themselves aren’t protected from direct access.

I believe I have a nicer solution to protect the properties from direct access: closures. The first few lines of code remain unchanged:

class RubyBean def initialize @listeners = [] end def register_listener ( l ) @listeners . push ( l ) unless @listeners . include ?( l ) end def unregister_listener ( l ) @listeners . delete ( l ) end

Now, observe how value is captured in the getter and setter blocks. The scope of the value local variable is the local block and the blocks passed in define_method . These blocks “close over” the value local variable.

def self . property (* properties ) properties . each do | p | value = nil define_method ( p ) { return value } define_method ( "#{p}=" ) do | new_value | return if value == new_value @listeners . each do | l | l . property_changed ( p , value , new_value ) end value = new_value end end end end

Finally, here is an example of how RubyBeans would be used (were they not a Java idiom shamelessly ported for illustration purposes). The impotent_name= method cannot affect the name property, it will create a name instance variable, unrelated to the property.

class SimpleBean < RubyBean property : name , : firstname def impotent_name =( new_name ) @name = new_name end end class LoggingPropertyChangeListener def property_changed ( property , old_value , new_value ) print property , " changed from " , old_value , " to " , new_value , "

" end end test = SimpleBean . new listener = LoggingPropertyChangeListener . new test . register_listener ( listener ) test . name = "Parker" test . firstname = "Charlie" test . firstname = "Maceo" test . unregister_listener ( listener )

Let me know if you spot something else!

Share this: Twitter

Facebook

Like this: Like Loading... Related