How to Add Functionality to Ruby Classes with Decorators

Decorators allow us to add behavior to objects in runtime and don’t affect other objects of the class. Decorators can be applied when you need to dynamically add and remove responsibility to a class. The decorator pattern is a helpful alternative to creating sub-classes. They give additional functionality to a class while still keeping the public API consistent. Let’s look at an example to understand the importance of Ruby Decorators. consider we have a Tattoo class with a price method that returns 300.
Class Tattoo def price 300 end end
Now we will add an extra color as a feature, and the price would be increased by 150 The simplest way is to create a TattooWithColour subclass that returns 450 in the price method.
class TattooWithColour < Tattoo def price 450 end end
Next, we need to represent a big tattoo that adds 200 to the price of our tattoos. We can represent this using a BigTattoo subclass of Tattoo.
class BigTattoo < Tattoo def price 500 end end
We could also have bigger sized tattoos and they may add further price to our BigTattoo. If we were to consider that these tattoos types could be used with colours, we would need to add BigTattooWithColour and BiggerTattooWithColour subclasses. With this method, we end up with total of 6 classes. Even Double that the number if you want to represent these combinations with extra designs on tattoo.

Inheriting dynamically with modules

To simplify our code, we may use modules to dynamically add behavior to our Tattoo class. Let’s write ColourTattoo and BigTattoo modules for this.
module ColourTattoo def price super + 150 end end
module BigTattoo def price super + 200 end end
Now we can extend our tattoo objects dynamically using the Object#extend method. tattoo = Tattoo.new tattoo.extend(ColourTattoo) tattoo.extend(BigTattoo) This is good improvement over our inheritance based implementation. Instead of having sub classes, we just have one class and 3 modules. If we needed to add extra design to the equation, we need just four modules instead of 12 classes.

Applying the decorator pattern

This module based solution has simplified our code greatly, but we can still improve it by using the decorator. We will consider a BiggerTatto as being formed by twice adding 150 to the cost of a Tattoo. We can’t do this by our module based approach. It would be tempting to call tattoo.extend(BigTattoo) twice to get BiggerTattoo. Extending module second time has no effect when we have already used extend ones. If we were to continue using the same implementation, we would need to have a BiggerTattoo module that returns super + 300 as the cost. Instead, we can use decorator that can be composed to build complex objects. We start with a decorator called BigTattoo that is a wrapper around a Tattoo object.
class BigTatto def initialize(tattoo) @tattoo = tattoo end
def price @tattoo.price + 150 end end
Bigger Tattoo can now be created by using this wrapper twice on a Tattoo object. tattoo = Tattoo.new big_tattoo= BigTattoo.new(tattoo) bigger_tattoo = BigTattoo.new(big_tattoo) We can similarly represent colour tattoo using a TattooWithColour decorator. Using just three classes, we are now able to represent 6 types of tattoo. With rich expertise in all facets of Ruby On Rails Development, we at Railscarma, offer you a wide range of services to help you implement a comprehensive personalized strategy to communicate with your prospects and your customers at the right time, through right channels. For more details Contact us.

Subscribe For Latest Updates

Related Posts

Leave a Comment

Your email address will not be published. Required fields are marked *

en_USEnglish