3

I recently came across the Replace Conditional with Polymorphism Refactoring while asking for elimination of if..else conditional in ruby.the link

Can anybody explain to me how can i implement the same in ruby?(A simple sweet code would do)

Community
  • 1
  • 1
Anony-mouse
  • 2,041
  • 2
  • 11
  • 23

2 Answers2

7

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

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
  • 2
    Plus two for Python (of the Monty variety) references. Micro-classes are so handy sometimes; I'm okay with extra classes for cleaner mainline code. – Dave Newton Jun 18 '15 at 20:33
  • 1
    Credit should go to Martin Fowler, I took the code straight from the book or rather the website. – Jörg W Mittag Jun 18 '15 at 20:34
  • Ah; I thought that looked familiar :) Makes me grin. – Dave Newton Jun 18 '15 at 20:35
  • @JörgWMittag and how do we use the code in a normal application for example `speed(norwegian_blue)` how would you implement same using your code? – Anony-mouse Jun 19 '15 at 12:35
  • You don't. You refactor it so that the `speed` method is a method of the `NorwegianBlue` class, just like I showed in my refactoring example, just like is explained in the refactoring book and on the refactoring website I linked to. – Jörg W Mittag Jun 19 '15 at 12:38
1

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.

user12341234
  • 6,573
  • 6
  • 23
  • 48
  • The guy who gave me answer in the link mentioned it as such anyways thank you I am reading the blog – Anony-mouse Jun 18 '15 at 18:48
  • Is it possible to achieve it without modifying the TrueClass /Object etc ? – Anony-mouse Jun 18 '15 at 18:55
  • This is relatively un-researched, but my intuition is no. The recommendation is to specifically use polymorphism. In Ruby polymorphism requires editing the object. Other languages have support for "parametric polymorphism", for example in Haskell with the feature "type classes". Type Classes (among other techniques) would allow you to add if_true/if_false to arbitrary types without modifying the core type itself. But alas, I don't know of a way to achieve this in Ruby. – user12341234 Jun 18 '15 at 18:59
  • the best way to achieve this degree of polymorphism in my opinion could have been the way of c++.But I am a noob in ruby so i cant even comment about haskell . – Anony-mouse Jun 18 '15 at 19:01
  • How's it achieved in C++? – user12341234 Jun 18 '15 at 19:02
  • simple functional polymorphism.Basically you have multiple functions with multiple type of argument(not multiple number like ruby) and you have if/else eliminated. – Anony-mouse Jun 18 '15 at 19:04
  • Oh, I see, this method relies on "multiple dispatch" which is tricky in Ruby (though, apparently possible as there's a gem out there to do it). I didn't realize C++ could dispatch based on the value of a boolean argument though. – user12341234 Jun 18 '15 at 19:09
  • I do not understand what multiple dispatch means,but C++ can have multiple methods with same name but with different type of arguments like one working for string another for integer etc – Anony-mouse Jun 18 '15 at 19:13
  • That's pretty much what multiple dispatch means. – user12341234 Jun 18 '15 at 19:14
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/80923/discussion-between-anony-mouse-and-user12341234). – Anony-mouse Jun 18 '15 at 19:15