rubyinheritancedelegation

DelegateClass vs Class Inheritance in Ruby


Can someone please provide some insight as to when to use delegation via DelegateClass (e.g. Seller < DelegateClass(Person)) and when to use class inheritance (e.g. Seller < Person) in ruby?

class Seller < DelegateClass(Person)
  def sales
     ...
  end 
end 

class Seller < Person 
  def sales
     ...
  end 
end 

When I was looking over the Ruby on Rails source on Github I found quite a few uses of DelegateClass.


Solution

  • There are a couple of differences that can help provide insight as to which approach to use.

    1) You can safely delegate to primitives (e.g. String), but cannot always safely inherit from them

    If you're building on top of Hash or String or Fixnum, you're safer using DelegateClass (or another delegator). For more on why, Steve Klabnik's cautioning is a good place to start).

    2) DelegateClass makes it easy to “convert” a more general object into a more specific one

    This makes it easier to accept an instance of a general object and make it behave in a way that's specific to your implementation:

    class Message < DelegateClass(String)
      def print
        upcase
      end
    end
    
    # […]
    
    def log(message)
      message = Message.new(message) unless message.is_a?(Message)
    end
    

    3) A gotcha: DelegateClass subclasses expect an instance of the delegated class as an argument to new

    This can make it tricky to “subclass” classes that you're handing to library code. For example, this is a fairly common practice that won't work out of the box with DelegateClass:

    class MyLogger < DelegateClass(ActiveSupport::Logger); end
    
    Foo::ThirdParty::Library.configure do |c|
      c.logger = MyLogger # no good
    end
    

    This doesn't work because our library expects to behave like most loggers and instantiate without arguments. This can be addressed by defining initialize and creating an instance of ActiveSupport::Logger, but probably not the right solution in this case.