In the object-oriented world, simple applications usually require small classes with static behaviors. Adding, modifying, and sharing those behaviors can be achieved by mixing in modules or inheriting from other classes at compile time.
However, more complex applications might require a particular instance of a class to gain additional functionality at runtime. To modify the behavior of an object dynamically, we can utilize the decorator design pattern.
When to Decorate
Decoration can be used to add behavior to any individual object without affecting the behavior of other objects of the same class. Essentially the existing object is being wrapped with additional functionality.
Some practical problems that can be solved by decoration are
- applying one or more UI elements to a specific UI widget at runtime.
- saving an ActiveRecord model in various ways based on conditionals in a Rails controller.
- adding additional information to data streams by pre/appending with additional stream data.
Implementations of Decorators in Ruby
There are several ways to implement the decorator pattern in Ruby, but I cover my 4 favorite ways:
- Class + Method Missing decorator
- Module + Extend + Super decorator
- Plain Old Ruby Object decorator
- SimpleDelegator + Super + Getobj decorator
Class + Method Missing Decorator
The benefits of this implementation are:
- can be wrapped infinitely using Ruby instantiation.
- delegates through all decorators.
- can use the same decorator more than once on the same component.
- transparently uses component’s original interface.
The drawbacks of this implementation are:
- uses method_missing.
- the class of the decorated object is the decorator.
Sample example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
|
Module + Extend + Super Decorator
The benefits of this implementation are:
- it delegates through all decorators.
- has all of the original interface because it is the original object.
The drawbacks of this implementation are:
- can not use the same decorator more than once on the same object.
- difficult to tell which decorator added the functionality.
Sample example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Plain Old Ruby Object Decorator
The benefits of this implementation are:
- can be wrapped infinitely using Ruby instantiation.
- delegates through all decorators.
- can use same decorator more than once on component.
The drawbacks of this implementation are:
- cannot transparently use component’s original interface.
Sample example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
SimpleDelegator + Super + Getobj
The benefits of this implementation are:
- can be wrapped infinitely using Ruby instantiation.
- delegates through all decorators.
- can use same decorator more than once on component.
- transparently uses component’s original interface.
- class if the component.
The drawbacks of this implementation are:
- it redefines class.
Sample example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
So far so good, Let decorate your way with Decorator Design Pattern. :)