4. @AtifTweets
Decorators
“Decorators attach additional responsibilities to an object dynamically. They provide a
flexible alternative to subclassing for extending functionality.” - Design Patterns:
Elements of Reusable Object-Oriented Software - GoF
5. @AtifTweets
Decorators
• Decorators: The Cure for Ugly Code
• Can easily add responsibility for any object
• Delegate any unknown method to object it
decorates
• Decorators only copy the type of the
component class not the behaviour
• Follow open and close philosophy = Open to
Extend … Close for Modifications
8. @AtifTweets
Why do we need this?
• Only thing constant is the CHANGE
• Inheritance Powerful but NOT Flexible or
Maintainable design pattern
• So we prefer Composition and Delegation
10. @AtifTweets
Coffeessimo
• A big coffee chain
• They have some basic coffee drink types
• And also customers can customize there
coffee by adding different condiments on
offer
• They have coffee types: HouseBlend,
DarkRoast, Espresso, Decaf
• Condiment types: Milk, Mocha, Soy, Whip
19. @AtifTweets
Exhibits
• Flavor of Decorators
• The primary goal of exhibits is to connect a
model object with a context for which it's
rendered
• Very often you'll likely want to add some
additional functionality. Such is the case with
exhibits. The additional functionality added
will extend (but not disrupt) the delegate
object.
20. @AtifTweets
Exhibits
• Wraps a single model instance.
• Is a true Decorator.
• Brings together a model and a context. Exhibits need
a reference to a "context" object—either a controller
or a view context—in order to be able to render
templates as well as construct URLs for the object or
related resources.
• Encapsulates decisions about how to render an
object. The tell-tale of an Exhibit is telling an object
"render yourself", rather than explicitly rendering a
template and passing the object in as an argument.
21. @AtifTweets
Example
class CarExhibit < Decorator
def initialize(car, context)
@context = context
super(car) # Set up delegation
end
def additional_info
"Some cars with 2 doors have a back seat, some
don't. Brilliant."
end
def render
@context.render(self)
end
end
class TextRenderer
def render(car)
"A shiny car!
#{car.additional_info}"
end
end
class HtmlRenderer
def render(car)
"A <strong>shiny</strong> car!
<em>#{car.additional_info}</em>"
end
end
class Car
def price
1_000_000
end
end
example courtesy mikepackdev.com
22. @AtifTweets
car = CarExhibit.new(Car.new, TextRenderer.new)
car.render #=> "A shiny car! Some cars with 2 doors have a back seat, some don't.
Brilliant.“
car.price #=> 1000000
car2 = CarExhibit.new(Car.new, HtmlRenderer.new)
car2.render #=> "A <strong>shiny</strong> car! <em>Some cars with 2 doors have a
back seat, some don't. Brilliant.</em>"
26. @AtifTweets
Presenters
• Presenters were originally formed as a more
composite-oreinted object, but modern day
presenters are more like decorators.
• Presenter deals with view and model
• Clean the view by moving the logic to the
presenter class
29. @AtifTweets
Difference b/w Exhibit and Presenters
• A key differentiator between exhibits and
presenters is the language they speak. Exhibits
shouldn't know about the language of the view
(eg HTML). Exhibits speak the language of the
decorated object. Presenters speak the language
of the view.
• Presenters and exhibits differ in their proximity
to the view. Presenters live very close to the view
layer. In fact, they are meant to be a
representation of the delegate object within the
view.
33. @AtifTweets
<%= link_to_if @user.url.present?, image_tag("avatars/#{avatar_name(@user)}", class:
"avatar"), @user.url %>
Helper Method
module UsersHelper
def avatar_name(user)
if user.avatar_image_name.present?
user.avatar_image_name
else
"default.png"
end
end
end
Condition
34. @AtifTweets
/app/presenters/user_presenter.rb
class UserPresenter
def initialize(user, template)
@user = user
@template = template
end
def avatar
@template.link_to_if @user.url.present?, @template.image_tag("avatars/#{avatar_name}", class:
"avatar"), @user.url
end
private
def avatar_name
if @user.avatar_image_name.present?
@user.avatar_image_name
else
"default.png"
end
end
end
35. @AtifTweets
/app/views/users/show.html.erb
<% present @user do |user_presenter|
%>
<div id="profile">
<%= user_presenter.avatar %>
<!-- Rest of view code omitted -->
</div>
<% end %>
module ApplicationHelper
def present(object, klass = nil)
klass ||= "{object.class}Presenter".constantize
presenter = klass.new(object, self)
yield presenter if block_given?
presenter
end
end
36. @AtifTweets
Further learning
• Head First Design Patterns di Eric Freeman,
Elisabeth Freeman, Kathy Sierra e Bert Bates
• Design Patterns in Ruby di Russ Olsen
• Design Patterns for Dummies
• Objects on Rails by Avdi Grimm
• http://mikepackdev.com/blog_posts/31-exhibit-vs
• http://railscasts.com/episodes/287-presenters-fro