When we need to reuse code we could use inheriatance – create a new child class and add/override some methods. But it could lead to complex inheritance chains and extra methods which is not needed in a new class. Do we have a better alternative? Sure! It’s object composition.
Favor ‘object composition’ over ‘class inheritance’. (Gang of Four 1995:20)
Object composition allows us to create more reusable pieces of code that could be combined together keeping our code simple and understandable.
Let’s take an example of making a new interface over existing object. For example, we need a SimpleQueue class which is based on Array but have more idiomatic interface – enqueuing using enq
method and dequeuing using deq
method.
To achieve it we could just define those methods and call push
and shift
methods inside them:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Method delegation is commonly used so Ruby Stdlib have some sugar which helps us doing delegation on a method by method basis. Using Forwardable we’re able to delegate methods in declarative way:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
So def_delegator
defines enq
method as a call to push
method on @queue
object. The same with deq
method.
There are also some array methods that could be useful for our queue object. We can easily delegate them all at once using def_delegators
:
1
|
|
Here is an example of using a new SimpleQueue class which has a new interface based on the existing one:
1 2 3 4 5 6 |
|
And it’s important that SimpleQueue class has only methods that it has to have and doesn’t have any additional methods inherited from Array.
Forwardable library also includes a SingleForwardable module which might be useful in API client libraries. Consider we created an API client library with the following interface:
1 2 |
|
It would be great to have an option to call API methods on a global client object instead of making a new client object every time we needed to do a request. We could use SingleForwardable to delegate methods to a global client object:
1 2 3 4 5 6 7 8 9 |
|
SingleForwardable defines methods on object which it’s called on. So def_delegators
declaration creates class methods in the example above and makes possible to perform API calls without instantiating an API client object manually:
1 2 |
|
If you were using both Forwardable and SingleForwardable in one class then you would need to call def_instance_delegator
and def_single_delegator
methods.
May objects be with you.