View
158
Download
1
Category
Tags:
Preview:
DESCRIPTION
Briefly about Decorator, Exhibits and Presenter Patterns
Citation preview
@AtifTweets
Exhibits and Presenters
@AtifTweets
Agenda
• Decorators• Exhibits• Presenters
@AtifTweets
Decorators
PresentersExhibits
@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
@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
@AtifTweets
@AtifTweets
The decorator pattern
@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
@AtifTweets
Example
example courtesy Head First Design Patterns
@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
@AtifTweets
Class Explosion
@AtifTweets
@AtifTweets
Dark Roast
cost()
@AtifTweets
Dark Roast
cost()
Mocha
cost()
@AtifTweets
Dark Roast
cost()
Mocha
cost()
Whip
Cost()
Whip Mocha Dark Roast coffee is ready!
@AtifTweets
Dark Roast
cost()
Mocha
cost()
Whip
Cost()
Cost
0.20
0.100.99
€1.29
@AtifTweets
Espresso
cost()
Whip
cost()
Dynamically created different coffees
0.10 0.90
Decaf
cost()
Mocha
cost()
0.20 1.20
€1.00€1.40
Whip Espresso Mocha Decaf
@AtifTweets
Exhibits
@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.
@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.
@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) endend
class TextRenderer def render(car) "A shiny car! #{car.additional_info}" endend
class HtmlRenderer def render(car) "A <strong>shiny</strong> car! <em>#{car.additional_info}</em>" endend
class Car def price 1_000_000 endend
example courtesy mikepackdev.com
@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>"
@AtifTweets
Presenters
@AtifTweets
a_view.html.rb
<% if entry.image_url.present? %> <%= render "/posts/picture_body", post: entry
%> <% else %>
<%= render "posts/text_body", post: entry %> <% end %>
@AtifTweets
VIEW MODEL
@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
@AtifTweets
VIEW MODELPRESENTER
Logic
Main Goal
@AtifTweets
Secondary Goal
VIEW MODELPRESENTER
Helpermethods
methods
@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.
@AtifTweets
Presenter in action
example courtesy railscast.com
@AtifTweets
@AtifTweets
/app/views/users/show.html.erb<div id="profile"> <%= link_to_if @user.url.present?, image_tag("avatars/#{avatar_name(@user)}", class: "avatar"), @user.url %> <h1><%= link_to_if @user.url.present?, (@user.full_name.present? ? @user.full_name : @user.username), @user.url %></h1> <dl> <dt>Username:</dt> <dd><%= @user.username %></dd> <dt>Member Since:</dt> <dd><%= @user.member_since %></dd> <dt>Website:</dt> <dd> <% if @user.url.present? %> <%= link_to @user.url, @user.url %> <% else %> <span class="none">None given</span> <% end %> </dd> <dt>Twitter:</dt> .....
@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 endend
Condition
@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
@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
@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-exhib
it-vs-presenter
• http://railscasts.com/episodes/287-presenters-from-scratch
@AtifTweets
Grazie!
Recommended