Wednesday, March 23, 2011

Ruby style question : blocks or inheritance ?

I have some classes that will do something based on some conditions . The conditions are sent as parameters to some methods . My questions is related to ruby coding style :

  • should the conditions be sent as lambdas/blocks , or as some objects that inherit from a condition class ?

  • which is more efficient in terms of OOP ?

Thanks !

From stackoverflow
  • I don't know which is more efficient from an execution time perspective, but blocks are more efficient from a user interface perspective. The person using your method just writes the condition in a block instead of having to define a whole new class, create an instance, and pass it to your method.

    Geo : What about a large project ? Long term projects ?
    Glomek : Saving yourself time and code on boilerplate stuff is even more important in large projects.
  • Lambdas are more the Ruby way — and besides that, why define a whole big class for something a simple lambda does just fine? Alternatively, you could pass conditions as a Hash like Rails tends to do.

    Geo : What if I need to setup my classes with more than one condition ? Where would I store all the conditions ? A namespace containing a hash and a whole bunch of conditions as hash values?
    Chuck : You haven't given any information about the structure of your program beyond that it "will do something based on some conditions," so I have no idea where you'd store anything.
  • The only case in wich I would use inheritance in this situation, is if the conditions themselves use many common code, that can be completely implemented in the superclass (without need to override anything in the subclases), but the practice of using inheritance for java style interfaces is not usual in Ruby.

    In any case, what you are doing is known as inversion of control and strategy patterns, read about that for more information. The key being the (good) decision of making the user of the code decide what the final behaviour of your code would be, as opossed to a configuration parameter or some other sort of branching in the implementation.

  • It's all negligible and it depends on what your actually doing and whether condition objects or procs could be cached, but in general blocks are the Ruby way and they are faster than object creation.

    Here's a useless benchmark:

    require 'benchmark'
    
    # Useless parent class
    class Condition; end
    
    # Useless inheritance.  Duck typing FTW.
    class AddCondition < Condition  
      def call
        1 + 1
      end
    end
    
    def with_object(condition)
      condition.call
    end
    
    def with_block
      yield
    end
    
    n = 100000
    Benchmark.bm(10) do |x|
      x.report("object:") do
        n.times do; with_object(AddCondition.new); end
      end
      x.report("block:") do
        n.times do; with_block { 1 + 1 };          end
      end
    end
    

    And the results:

                    user     system      total        real
    object:     0.090000   0.000000   0.090000 (  0.087227)
    block:      0.060000   0.000000   0.060000 (  0.063736)
    
    Geo : We could explain that because creating an object is more expensive than adding two values together.
    Ryan McGeary : @Donovan, it's more than just adding two values together; the block is created as well. There is still overhead in keeping a reference to the stack that is used by the _closure_.
    Ryan McGeary : ...but yes, Object creation is more expensive.

0 comments:

Post a Comment