From: Phrogz Date: 2008-02-19T07:09:55+09:00 Subject: Useful uses for instance-specific methods. I ran into two specific cases this weekend where singleton methods made my code feel a lot cleaner. For your enjoyment, I'll show them both here. Here's the first version of the code: class WordTreeNode attr_accessor :parent attr_accessor :text_value attr_accessor :source_value def initialize( text_value=nil ) @children = [] @text_value = text_value end def <<( kid ) @children << kid kid.parent = self end def text @text_value || @children.map{ |kid| kid.text }.join end def source @source_value || (@parent && @parent.source) end end root = WordTreeNode.new root.source_value = "Bob" root << (greeting = WordTreeNode.new) root << (query = WordTreeNode.new) greeting << WordTreeNode.new( "Hi there! " ) query << WordTreeNode.new( "What are you " ) query << WordTreeNode.new( "doing tonight?" ) p root.text #=> "Hi there! What are you doing tonight?" p query.source #=> "Bob" Now, we clean it up a little bit to simplify the implementation of #source and #text: class WordTreeNode attr_accessor :parent def initialize @children = [] end def <<( kid ) @children << kid kid.parent = self end def text @children.map{ |kid| kid.text }.join end def source @parent && @parent.source end end class RootNode < WordTreeNode attr_accessor :source def initialize( source ) super() @source = source end end class LeafNode < WordTreeNode attr_accessor :text def initialize( text ) super() @text = text end end root = RootNode.new( "Bob" ) root << (greeting = WordTreeNode.new) root << (query = WordTreeNode.new) greeting << LeafNode.new( "Hi there! " ) query << LeafNode.new( "What are you " ) query << LeafNode.new( "doing tonight?" ) But we can also choose not to have those as separate classes: class WordTreeNode attr_accessor :parent def initialize @children = [] end def <<( kid ) @children << kid kid.parent = self end def text @children.map{ |kid| kid.text }.join end def source @parent && @parent.source end def self.leaf( text ) node = self.new node.instance_eval{ @text = text def text @text end } node end end root = WordTreeNode.new def root.source "Bob" end root << (greeting = WordTreeNode.new) root << (query = WordTreeNode.new) greeting << WordTreeNode.leaf( "Hi there! " ) query << WordTreeNode.leaf( "What are you " ) query << WordTreeNode.leaf( "doing tonight?" ) p root.text #=> "Hi there! What are you doing tonight?" p query.source #=> "Bob" The above code is less convoluted in Io, where I actually performed this. (In Io you don't have to set a string value and then write a method that returns that value...you simply set the "text" or "source" message on the instance to *be* the string.) But hopefully it shows you an interesting case where duck typing and per-instance-methods can overlap to easily handle the edge cases.