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
To achieve it we could just define those methods and call
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
enq method as a call to
push method on
@queue object. The same with
There are also some array methods that could be useful for our queue object. We can easily delegate them all at once using
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:
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:
If you were using both Forwardable and SingleForwardable in one class then you would need to call
May objects be with you.