Ruby, the Consummate Delegator - Part 2
Back in June, I posted about using Ruby's method_missing for simplified delegation.
Well, I might have known Ruby would have even more options for handling delegation.
The following achieves the same result without having to implement method_missing. DelegateClass implements method_missing for you, along with some other conveniences.
require 'delegate'
class CardDeck < DelegateClass(ShuffleArray)
def initialize(cards)
super(ShuffleArray.new(cards))
end
end
Now if we wanted to selectively delegate certain methods to certain objects, we could instead extend our class with Forwardable. Here we avoid inheritance at the expense of some repetition as we'd have to call def_delegators for each method that we want to expose from ShuffleArray.
require 'forwardable'
class CardDeck
extend Forwardable
def initialize(cards)
@cards = ShuffleArray.new(cards)
end
def_delegators :@cards, :shuffle, :size,
:[], :find,
:reverse, :each,
:sort_by
end
For one thing we're somewhat back to writing those pesky delegation methods that we hoped to avoid in the first place. def_delegators certainly makes it a bit easier than doing the same thing in say C# or Java, and I suppose we shouldn't be too lazy. But we also could forget to pass in some methods and if Array or ShuffleArray gets redefined at runtime, then we won't delegate to them like we would with method_missing.
I'll grant you this whole CardDeck example is a bit contrived and if I thought about it a bit more I could probably come up some better examples of when to use these different approaches "in the real world."
For example, Forwardable could be useful for writing an adapter for a class that needs to conform to the same interface as another, or for building a façade to expose a subset of a delegate's interface.
I'd certainly be interested in hearing about how others have used these nifty features in Ruby.