What is Replace Conditional with Polymorphism Refactoring? How is it implemented in Ruby?

后端 未结 2 901
清歌不尽
清歌不尽 2021-01-20 14:43

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

相关标签:
2条回答
  • 2021-01-20 15:16

    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.

    0 讨论(0)
  • 2021-01-20 15:31

    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

    0 讨论(0)
提交回复
热议问题