When creating a new Ruby class, it can be helpful to use composition over
inheritance. Let’s look at an example of an
Image has a
File, but we don’t want the
Image to extend
File. Why not? We don’t want
Image to have the full interface of
File; just a small subset of it. This is
a perfect use case for composition and method delegation.
Here’s what our
Image class looks like right now. It contains the
object we’re interested in.
class Image def initialize(image_path) @file = File.open(image_path) end end
Next, we would like to expose a few of the
File methods on
#close. One way to do that is by writing our own methods that
pass the message along.
require 'forwardable' class Image def initialize(image_path) @file = File.open(image_path) end def path @file.path end def read @file.read end def close @file.close end end
This quickly gets repetitive and verbose.
Fortunately, Ruby provides an easy means of delegating methods to other objects.
We’ll extend the
Forwardable module, and then use
def_delegator to hook up
class Image extend Forwardable def initialize(image_path) @file = File.open(image_path) end def_delegators :@file, :path, :read, :close end
We’ve accomplished the same functionality in fewer lines, and made it more readable.
Unfortunately, I find Ruby’s core method delegation unintuitively named and
implemented. It’s confusing to remember the
Forwardable module gives us method
delegation, and that the method is named
def_delegators. I wrote this small
post so I can refer to it later, when I need a refresher on how to do method
delegation in Ruby.