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
.
Ruby supports only single class inheritance, it does not support multiple class inheritance but it supports mixins. The mixins are designed to implement multiple inheritances in Ruby, but it only inherits the interface part.
In Ruby, single class inheritance is supported, which means that one class can inherit from the other class, but it can't inherit from two super classes. In order to achieve multiple inheritance, Ruby provides something called mixins that one can make use of.
Example# Multiple inheritance is a feature that allows one class to inherit from multiple classes(i.e., more than one parent). Ruby does not support multiple inheritance. It only supports single-inheritance (i.e. class can have only one parent), but you can use composition to build more complex classes using Modules.
In Ruby, we use the < operator to create inheritance relations. The Human class inherits from the Being class. The super method calls the constructor of the parent class.
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.
delegates model different behaviors of Person
based on the context. e.g. the same person could be a seller in one context or a buyer in a different context. Inheritance is more rigid: a Bear
and Tiger
inherit from Animal
, but an instance of Animal
would never need to sometimes behave like a Bear
and sometimes behave like a Tiger
. An instance of a descendent of Animal
is either one or the other.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With