The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Patterns in Ruby: Template Method

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Christopher Williams

Posts: 88
Nickname: sgtcoolguy
Registered: Apr, 2006

Christopher Williams is a Ruby, Rails and Java programmer
Patterns in Ruby: Template Method Posted: Oct 27, 2006 11:49 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Christopher Williams.
Original Post: Patterns in Ruby: Template Method
Feed Title: Late to the Party
Feed URL: http://cwilliams.textdriven.com/xml/rss20/feed.xml
Feed Description: Ruby. Rails. Stuff.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Christopher Williams
Latest Posts From Late to the Party

Advertisement

I've been a fan of the work that was done by the Gang of Four on Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Professional Computing Series), Martin Fowler's Refactoring: Improving the Design of Existing Code and the bridge book by Joshua Kerievsky, Refactoring to Patterns. I haven't seen a lot of information out there on how Ruby changes the game: ways to apply these patterns using Ruby idioms, new patterns that show up, patterns that fall away. So I've decided that as I go along I'll try and document the new twists as I see them.

Today's article is the twist on the Decorator pattern. The Decorator pattern wraps the original object in a new one which will add functionality to some of the methods and then delegate to the original object. The prototypical example is decorating a window object.

A Decorator Example

public innterface Window {
  public void draw();
}

In Java, we'd probably use a decorator to add scroll bars.

public Class VerticalScrollWindow implements Window {
  private Window window;

  public VerticalScrollWindow(Window window) {
    this.window = window;
  }

  public void draw() {
    drawScrollBar();
    window.draw();
  }
}

The basic concept is that we'll usually want to be adding some behavior around a particular method call to extend behavior, while retaining the same interface.

Ruby

In ruby, we have a number of options to achieve this pattern. First, let's define our original Window in Ruby code:

class Window
  def draw
    # do some drawing here...
  end
end

Translating the pattern literally

Given ruby's duck-typing nature, we could easily create a VerticalScrollWindow that wraps the original Window when we create the original window object, and pass that around. In fact we could patch only the single method and add a method_missing implementation that always delegated to the original Window.

class VerticalScrollWindow

  def initialize(window)
    @window = window
  end

  def draw
    draw_vertical_scrollbar
    window.draw
  end

  def method_missing(method, *args, &block)
    @window.send(*args, &block)
  end
end

This approach could be evolved to a much higher level using method_missing tricks and dynamic modifications. One could create a generic Proxy class which took a target class and intercepted all method calls, executing pre- and post- method blocks for specific methods. I'll leave that as an exercise for the reader for now...

Using Alias

Another option we have is to "monkeypatch" the original class (or specific instances of it). The idea here is to rename the old implemntation of the method, insert a new implementation and have that refer to the renamed original.

class Window
  def draw
    # do some drawing here...
  end

  # some code...

  alias :original_draw :draw
  def draw
    draw_vertical_scrollbar
    original_draw
  end
end

Building pre and post hooks

Our last option is to build pre and post method hooks into the original class definition. Obviously, this approach requires the original class' author to explicitly build in callback hooks. This approach can be found in Capistrano (you can add tasks which get executed before or after well known tasks), or in ActiveRecord (lifecycle type callbacks - i.e. before_save, after_destroy).

This option is a bit more advanced and differs in the approach taken. To learn how Capistrano does it, dive into capistrano/actor.rb, line 118. Each task is defined as a method which explicitly calls before and after methods if they exist.

For ActiveRecord, please refer to activerecord/callback.rb. ActiveRecord goes a little further by allowing class level methods to add pre- and post- code blocks to be executed which will be inherited down the class hierarchy. They also allow instance level methods to be defined for each hook which would not be inherited.

Read: Patterns in Ruby: Template Method

Topic: Sun gets Ruby-fied Previous Topic   Next Topic Topic: 37Signals' Getting Real book is now Free on-line

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use