From: patrick-may@... (Patrick May) Date: 2002-09-14T08:03:10+09:00 Subject: Re: not grasping the method overloading/multi-dispatch thing Philipp Meier wrote in message news:<20020913101910.GC17997@o-matic.net>... > On Fri, Sep 13, 2002 at 02:22:25PM +0900, Patrick May wrote: > > I'd be curious to see code that > > > > a. calls for overloading on type > > b. couldn't be handled by pushing code up to the classes of the > > argument objects > > Because of Rubys dynamic nature this is IMHO always possible. But is it > good design? You will agree, that, as an example, code that formats a > business object for reporting certainly does not belong into the business > object class but in a reporting class. Code should execute in the namespace close to its data. That said, Ruby lets you use modules to clearly separate the code within a given namespace. See below. > A solution I proposed before is > extracting this kind of methods into a seperate classes which go along > the classes you propose the methods belong into. The polymorphic > dispatching logic then dispatch by type (untested): > > class PolyTest > @@receivers = { :to html => { String => StringAdapter, > Integer => IntegerToHTML }, > :to rtf => { String => StringAdapter, > Integer => IntegerToRTF } > } > > def dispatch(method, index, *args) > r = @@receivers[method][args[index].type] > r.send(method, self, *args) > end > > def to html(doc, object) > dispatch(:to html, 1, doc, object) > end > > def to rtf(doc, object) > dispatch(:to rtf, 1, doc, object) > end > > def test > to html("", "Test") # => calls StringAdapter.to html("", "Test") > to rtf ("", "Test") # => calls StringAdapter.to rtf("", "Test") > to html("", 1) # => calls IntegerToHTML.to html("", 1) > to rtf ("", 1) # => calls IntegerToRTF.to rtf("", 1) > end > end > > class StringAdapter > def to html(doc, string) > doc << "String: #{string}
" > end > def to rtf(doc, string) > ... > end > end > > class IntegerToHTML > def to html(doc, i) > doc << "Integer: #{i}
" > end > end > > class IntegerToRTF > def to rtf(doc, i) > ... > end > end or, using modules: class PolyTest def test "Test".to_html # =~ StringAdapter.to html("", "Test") "Test".to_rfc # =~ StringAdapter.to rtf("", "Test") 1.to_html # =~ IntegerToHTML.to html("", 1) 1.to_rtf # =~ IntegerToRTF.to rtf("", 1) end end module FormatingMethods def to html "<#{ self.tag }>#{ self.class.to_s }:" + " #{self.to_s}
" end def to rtf ... end end class String include FormatingMethods def tag "b" end end class Integer include FormatingMethods def tag "i" end end Which is the preferable design? Note the differences from the perspective of a user of the api (PolyTest.test): No mediating functions. No arguments to forget when calling :to_html/:to_rtf. No binding between the output stream and :to_html/:to_rtf, though I realize your example could be changed to avoid that binding. The implementation is also short (~10 fewer LOC) and simple -- not a single conditional to implement this behavior! Once you've decided to create an object, you shouldn't need anymore conditionals on that type. It's nice to have such conditionals, and I use them in my code, but they are refactoring targets to me. ~ Patrick