I recently came across the Replace Conditional with Polymorphism Refactoring while asking for elimination of if..else
conditional in ruby.the l
I think I would format the bolded phrase a little differently, i.e.: Refactor your code to replace the conditional with polymorphism.
If that is indeed what the comment is supposed to mean, then Yehuda Katz has a great post giving an example in ruby: http://yehudakatz.com/2009/10/04/emulating-smalltalks-conditionals-in-ruby/
Basically, the argument is that an if/else statement exists to execute different code based on the value of a boolean. It requires special syntax, and is limited to only the types TrueClass/FalseClass (or Object/NilClass if you're being lax about truthiness). Dynamic dispatch on the other hand performs the same operation of selecting branches of code to execute based on the value of an object, but it does not rely on specialized syntax, nor is it limited to any specific group of types.
The Replace Conditional with Polymorphism Refactoring is rather simple and it is pretty much exactly what it sounds like. You have a method with a conditional like this:
def speed
case @type
when :european then base_speed
when :african then base_speed - load_factor * @number_of_coconuts
when :norwegian_blue then if nailed? then 0 else base_speed(@voltage) end
end
and you replace it with polymorphism like this:
class European
def speed
base_speed
end
end
class African
def speed
base_speed - load_factor * @number_coconuts
end
end
class NorwegianBlue
def speed
if nailed? then 0 else base_speed(@voltage)
end
end
You can apply the Refactoring again to NorwegianBlue#speed
by creating a subclass of NorwegianBlue
:
class NorwegianBlue
def speed
base_speed(@voltage)
end
end
class NailedNorwegianBlue < NorwegianBlue
def speed
0
end
end
Voilà, all your conditionals are gone.
You might ask yourself: does this always work? Can I always replace an if
with message dispatch? And the answer is: yes, you can! In fact, if you didn't have if
, you can implement it yourself using nothing but message dispatch. (This is, in fact, how Smalltalk does it, there are no conditionals in Smalltalk.)
class TrueClass
def iff(thn:, els: ->{})
thn.()
end
def &
yield
end
def |
self
end
def !
false
end
end
class FalseClass
def iff(thn:, els: ->{})
els.()
end
def &
self
end
def |
yield
end
def !
true
end
end
(3 > 4).iff(thn: ->{ 'three is bigger than four' }, els: ->{ 'four is bigger than three' } )
# => 'four is bigger than three'
true.& { puts 'Hello' }
# Hello
true.| { puts 'Hello' }
# => true
Also relevant: the Anti-IF Campaign