Philipp Meier meier@meisterbohne.de wrote in message news:20020913101910.GC17997@o-matic.net…
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.tag}> #{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
···
On Fri, Sep 13, 2002 at 02:22:25PM +0900, Patrick May wrote: