NOTE:This blog had a good run, but is now in retirement.
If you enjoy the content here, please support Gregory's ongoing work on the Practicing Ruby journal.

Issue 1.26: Structural Design Patterns

2011-12-27 18:00, written by Gregory Brown

Originally published as part of the first volume of the Practicing Ruby newsletter on February 28, 2011. Most of these issues draw inspiration from discussions and teaching sessions at my free online school, Mendicant University.

In this two part series, I’ve been looking at the classical design patterns laid out by the Gang of Four and exploring their relevance to modern day Ruby programming. Rather than teaching the design patterns themselves, my goal is to give practical examples using real code so that you can consider what the strengths and weaknesses of these techniques are.

In this issue, I’ll be focusing on the structural design patterns, all of which have to do with the relationships between clusters of code. The seven patterns listed below are the ones we’ll focus on in this article.

  • Adapter
  • Bridge
  • Composite
  • Proxy
  • Decorator
  • Facade
  • Flyweight

An important thing to keep in mind is that while the original GoF book was written with C++ and Smalltalk examples, none of the patterns were written with Ruby’s semantics in mind. This makes it necessary to use a little poetic license to explore how these patterns apply in Ruby. That having been said, the examples we’ll look at attempt to preserve the spirit of the patterns they implement even if they’re not semantically identical.

Adapter

An Adapter is used when you want to provide a unified interface to a number of different objects that implement similar functionality. This pattern is easy to spot in the wild for a Ruby developer, as most of us have to use ActiveRecord in our day to day work. Under the hood, ActiveRecord talks to a large amount of different databases, but wraps them up in a common interface its implementation code can avoid worrying about their individual differences. This is exactly the sort of thing the Adapter pattern is useful for.

Increasingly, Rubyists are finding this pattern to be useful in other settings as well. In particular, things like Intridea’s multi_json gem allow libraries and applications to be built against an abstract interface rather than requiring some particular JSON library that might conflict with other dependencies. Inspired by the ideas behind multi_json, a Mendicant University student (Mitko Kostov) built a similar adapter library for Markdown processors called Marky. The base implementation is very simple, and gives us a good opportunity to look at one way to implement an Adapter in Ruby from the ground up.

The basic idea is that we want to be able to use a common interface while easily configuring which backend is used under the hood. The following example shows how Marky might be used.

  
# using RDiscount as a backend
Marky.adapter = :rdiscount

Marky.to_html("hello world") #=> "<p>hello world</p>

# using BlueCloth as a backend
Marky.adapter = :bluecloth

Marky.to_html("hello world") #=> "<p>hello world</p>"

To make this work, it is necessary to map the name of an adapter to a class which wraps the underlying engine and implements a to_html() method. The code that actually wires up the adapter is shown below.

module Marky
  extend self

  def adapter
    return @adapter if @adapter
    self.adapter = :rdiscount
    @adapter
  end
   
  def adapter=(adapter_name)
    case adapter_name
    when Symbol, String
      require "adapters/#{adapter_name}"
      @adapter = Marky::Adapters.const_get("#{adapter_name.to_s.capitalize}")
    else
      raise "Missing adapter #{adapter_name}"
    end
  end

  def to_html(string)
    adapter.to_html(string)
  end
end

While this uses a tiny bit of dynamic Ruby magic to look up the right module name, when we see the actual adapters, it all comes together.

# adapters/bluecloth.rb

require "bluecloth"

module Marky
  module Adapters
    module Bluecloth
      extend self
      def to_html(string)
        ::BlueCloth.new(string).to_html
      end
    end
  end
end
# adapters/rdiscount.rb

require "rdiscount"

module Marky
  module Adapters
    module Rdiscount
      extend self
      def to_html(string)
        ::RDiscount.new(string).to_html
      end
    end
  end
end

Since all the adapters implement a to_html() method that share a common contract, Marky.to_html() will work regardless of what adapter gets loaded. The win here is that if that libraries, applications and frameworks rely on adapters rather than concrete implementations, the choice of which engine to use can be done differently in different environments.

While not every problem domain needs added level of indirection that Adapters introduce, they can come in handy when there are several competing implementations solving a common problem but you don’t want to forced to choose one over the other.

Bridge

The idea behind a Bridge is to place a physical separation between the interface of a given type of object and its implementation. In languages in which the type system gets in the way, this kind of pattern is important for reducing the proliferation of various type permutations by making hierarchies of interfaces and implementations orthogonal to one another. If that sounds like academic jibberish to you, there is a SourceMaking article that might help sort the concepts out for you a bit.

I had a hard time thinking through where this pattern has its place in Ruby, mainly because there is a built in separation of interface and implementation in Ruby via duck typing semantics. The best example I could come up with was to port the painfully complex C++ example that SourceMaking uses in their article to something that is a bit more natural looking in Ruby. Read it over, and think about what it might be gaining us as you go.

##Concrete Implementations

class BasicTimeData
  def initialize(hour, minutes)
    @hour     = hour
    @minutes  = minutes
  end

  def formatted_output
    "Time is #{@hour}:#{@minutes}"
  end
end

class TimeWithMeridianData
  def initialize(hour, minutes, meridian)
    @hour     = hour
    @minutes  = minutes
    @meridian = meridian
  end

  def formatted_output
    "Time is #{@hour}:#{@minutes} #{@meridian}"
  end
end

##Bridge

module TimeFormatter
  def to_s
    @time_data.formatted_output
  end
end

## Abstract Objects linked to Concrete Implementations through Bridge

class BasicTime
  include TimeFormatter
  
  def initialize(*a, &b)
    @time_data = BasicTimeData.new(*a, &b)    
  end
end

class TimeWithMeridian
  include TimeFormatter

  def initialize(*a, &b)
    @time_data = TimeWithMeridianData.new(*a, &b)
  end
end

## Example Usage

time1  = BasicTime.new("10","30")
time2  = TimeWithMeridian.new("10","30","PM")

[time1, time2].each { |t| puts t }

While it might just due to the nature of the example, I feel this code is still quite contrived, and that hides its benefits. The takeway here is mostly that it’s possible for both BasicTimeData and TimeWithMeridianData to change without breaking BasicTime and TimeWithMeridian, as long as TimeFormatter is updated. Similarly, a change in the needs of BasicTime and TimeWithMeridian will not affect the concrete implementations of the data structures, as long as the bridge provides the necessary wiring.

The thing that I struggle with in this pattern is understanding what unique behaviors the BasicTime and TimeWithMeridian objects could implement that justify their existence. Is this pattern an artifact of static languages that isn’t really needed in Ruby? Or am I just missing some important detail that could be cleared up with a good example or two? Feel free to leave a comment letting me know what you think.

Composite

The Composite pattern is useful when you want to treat a group of objects as if it were a single, unified object. To explore this pattern, we can look at some experimental code I wrote for Prawn which was designed to make it possible to treat all objects drawn in the document as compositions of primitive PDF instructions. We can start with an example of rendering a curve, working from the outside in.

When curve() is called on a Prawn drawing, the necessary PDF content is generated as a side effect and the user does not need to do anything with the return value of that method. However, if someone wanted to play with the internals, they could call curve!() instead and get themselves an object that implements a to_pdf() method, as shown in the example below.

chunk = canvas.curve!(:point1 => [100,100],
                      :point2 => [50,50], 
                      :bound1 => [60,90],
                      :bound2 => [60,90])

puts chunk.to_pdf

...........................................................OUTPUTS..

  100.000 100.000 m
  60.000 90.000 60.000 90.000 50.000 50.000 c

We can catch a glimpse of how this to_pdf() method is actually implemented via several composed primitive objects by taking a look at the source for the curve!() method.

def curve!(params)
  chunk(:curve, params) do |c|
    [ move_to!(:point => c[:point1]),
      curve_to!(:point => c[:point2],
                :bound1 => c[:bound1],
                :bound2 => c[:bound2]) ]

  end
end

In the above, chunk() is a helper method which builds a Prawn::Core::Chunk object, which serves as the composite object in this system. We’ll look at how chunks are implemented in a bit, but note that the block given to the chunk() method defines what the individual chunk is composed of. In this case, a curve consists of two subchunks, one responsible for generating a move instruction, and one for rendering a curve from the current drawing location to another point. The definitions for both of those methods are shown below.

def move_to!(params)
  chunk(:move_to, params) do |c|
    raw_chunk("%.3f %.3f m" % c[:point])
  end
end

def line_to!(params)
  chunk(:line_to, params) do |c|
    raw_chunk("%.3f %.3f l" % c[:point])
  end
end

Similar to chunk!(), these methods can called directly, producing an object with a to_pdf() method, as shown below.

chunk = canvas.move_to!(:point => [100,100])
puts chunk.to_pdf

chunk2 = canvas.curve_to!(:point => [50,50],
                          :bound1 => [60,90],
                          :bound2 => [60,90])

puts chunk2.to_pdf

# combined output is identical to calling curve!() directly.

Through these two methods, we encounter the leaf nodes in our composition, Prawn::Core::RawChunk objects. These objects are where the actual text that is in our to_pdf() is stored. With that in mind, we can now look at the actual objects that this graphics system is built on.

module Prawn
  module Core
    class RawChunk
      def initialize(content)
        @content = content
        @command = :raw_pdf_text
        @params = {}
      end

      attr_accessor :content, :command, :params
      alias_method :to_pdf, :content
    end

    class Chunk
      def initialize(command, params={}, &action)
        @command = command
        @params = params
        @action = action
      end

      attr_reader :command, :params, :action

      def content
        action.call(self)
      end

      def to_pdf
        case results = content
        when Array
          results.map { |sub_chunk| sub_chunk.to_pdf }.join("\n")
        when Prawn::Core::Chunk, Prawn::Core::RawChunk
          results.to_pdf
        else
          raise "Bad Chunk: #{results.class} not supported"
        end
      end
    end
  end
end

From this we see that a chunk’s content can be an array of children, a chunk, or a raw chunk object. The to_pdf() method is responsible for traversing downwards through the composition until it reaches the raw chunk objects which simply return the raw content. But because the APIs match, we can look at the chunks at any level of the system and burrow down to their raw data.

While this might be a bit of an intense example, it shows how the Composite pattern can be used to make a complex set of objects look and feel as if they were a single unified entity. It may be a bit much to take in on a first glance, but try to think of how you could apply these ideas to your own code and you might gain some useful insights.

Proxy

A Proxy is any object that acts as a drop-in replacement object that does a bit of work and then delegates to some other underlying object. This is another pattern that’s easy to recognize for Rails developers, because it is used extensively in ActiveRecord associations support. The following example shows a typical interaction between a base model and one of its associations.

quiz = Quiz.first
quiz.questions.class #=> Array

quiz.questions.count #=> 10

quiz.questions.create(:question_text => "Who is the creator of Ruby?",
                      :answer        => "Matz")

quiz.questions.count #=> 11

quiz.questions[-1].answer #=> "Matz" 

While the questions association claims to be an array, it also provides some of the common helpers found in ActiveRecord::Base. If we stick to the core idea and ignore some of the Rails specific details, creating such an association proxy is fairly easy to do using Ruby’s delegate standard library. The code below more or less does the trick.

require "delegate"

class Quiz
  def questions
    @questions                  ||= HasManyAssociation.new([])
    @questions.associated_class ||= Question

    @questions
  end
end

class Question
  def initialize(params)
    @params = params
  end

  attr_reader :params

  def answer
    params[:answer]
  end
end

class HasManyAssociation < DelegateClass(Array)
  attr_accessor :associated_class

  def initialize(array)
    super(array)
  end

  def create(params)
    self << associated_class.new(params)
  end
end

quiz = Quiz.new

# grab the proxy object
questions = quiz.questions


# use custom create() method on proxy object

questions.create(:question_text => "Who is the creator of Ruby?",
                 :answer        => "Matz")
questions.create(:question_text => "Who is the creator of Perl?",
                 :answer        => "Larry Wall")


# use array like behavior on proxy object

p questions[0].answer #=> "Matz"
p questions[1].answer #=> "Larry Wall"
p questions.map { |q| q.answer }.join(", ") #=> "Matz, Larry Wall"

Interestingly enough, while Ruby provides a standard library for building Proxy objects, most people tend to implement them in a different way, through the use of an explicit method_missing() hook on a blank slate object such as Ruby 1.9’s BasicObject. For example, we could have written our HasManyAssociation code in the manner shown below and things would still work as expected.

class HasManyAssociation < BasicObject
  attr_accessor :associated_class

  def initialize(array)
    @array = array
  end

  def create(params)
    self << associated_class.new(params)
  end

  def method_missing(*a, &b)
    @array.send(*a, &b)
  end
end

Without looking at the source, I’m almost sure that Rails does something similar to this, because doing some_association.class returns Array rather than the name of the proxy object. This is the only noticeable difference on the surface between this approach and the DelegateClass approach.

Personally, I’ve written proxies in both ways, and I tend to prefer the DelegateClass() approach slightly, simply because it’s more explicit and doesn’t require me to explicitly define a method_missing() hook. But on the other hand, we can see that rolling your own proxy implementation is trivial in Ruby, and some may prefer the self contained nature of doing the delegation work yourself. It’d be interesting to hear what readers have to say on this topic, so please feel free to post to the mailing list if you prefer one approach over the other.

Decorator

While there is a more clear distinction between a Decorator and a Proxy in static languages, in Ruby the two concepts almost merge, except that a Decorator is used almost exclusively for the purpose of adding / extending behavior of a target object, and a Proxy is a more general concept.

Since I’ve already written up a cool example of using decorators on this blog, I think what I’ll do is point you over there in the interest of keeping this article from being even more incredibly long than it already is. Check out the Decorator Delegator Disco if you want to see some interesting code samples that implement this pattern.

Facade

The Facade pattern simplifies how users interact with a codebase by implementing an interface that hides many implementation details for the most common behaviors needed by consumers. Looking at it from the outside, a perfect example of this is the open-uri standard library. When using open-uri, it is possible to do a simple HTTP get request with just a single line of code, as shown below.

require "open-uri"

puts open("http://www.google.com").read

The purpose of open-uri is to make reading a resource via an HTTP GET request look and feel like opening an ordinary file. This is a very common use case, so open-uri makes it easy to work with. To see what this facade is hiding, we can write the equivalent code using the libraries it wraps, Net::HTTP and URI.

require 'net/http'
require 'uri'

url = URI.parse('http://www.google.com')

res = Net::HTTP.start(url.host, url.port) {|http|
  http.get('/index.html')
}

puts res.body

While the code above isn’t especially difficult to read, it certainly feels like more work than the previous example. This is primarily because of the flexibility and functionality tradeoff between the two approaches. The open-uri library can afford to be much more high level and limited in scope because its sole purpose is to help make a single particular task easier. On the other hand, Net::HTTP and URI are both complex tools that can be used for a number of different things. The use of the Facade pattern allows for both kinds of objects to coexist peacefully within a single system.

It’s worth keeping in mind that pretty much every DSL you encounter is a Facade of some form or another. If you’re interested in seeing how simple interfaces can mask highly complex networks of objects, consider doing a source dive into your favorite tool that utilizes a domain specific language, such as Rake, RSpec, or Sinatra. You’ll find a number of different techniques at work depending on which project you explore, but all have the common characteristic of providing a simplified way to interact with a deep system.

Flyweight

The Flyweight pattern is a way to represent what would seem like a large amount of data in a lightweight way. This is one of those patterns that mostly goes away in Ruby due to built in language constructs, but it’s worth taking a look at just for the sake of completeness.

The wikipedia article linked above discusses font metrics as a common application of the flyweight pattern, in which you may want to associate each character in a string with a large amount of information describing how that character should be rendered. The basic idea is that it’d be far too inefficient memory-wise to create a new instance of font metrics data for every character in a document. So instead, using the Flyweight pattern, it is possible to map the index of characters in a string to a single instance for each unique character, vastly reducing the amount of memory consumed. This is a problem I’ve actually had to solve before within Prawn, but it’s a bit of a tough one demonstrate briefly if we attempt to show real code.

However, if we stub out the actual font metrics generation, it’s easy to see that this problem can be solved by wrapping a simple Ruby hash that has an initializer block.

class FontMetrics

  def initialize(string)
    @string = string
  end

  def [](index)
    glyph_data[@string[index]]
  end

  def glyph_data
    @glyph_data ||= Hash.new { |h,k| h[k] = metrics_for(k) }
  end

  # stubbed out, but would typcially refer to something
  # largish and time consuming to generate
  # 
  def metrics_for(k)
    { :bbox => [rand(100), rand(100), rand(100), rand(100)] }
  end

end

string = "Hello World"

metrics = FontMetrics.new(string)

p metrics[0]  #=> {:bbox=>[86, 44, 88, 31]}
p metrics[2]  #=> {:bbox=>[52, 7, 38, 98]}
p metrics[3]  #=> {:bbox=>[52, 7, 38, 98]}
p metrics[-2] #=> {:bbox=>[52, 7, 38, 98]}

p metrics[2].object_id == metrics[3].object_id #=> true

p metrics[0] == metrics[1] #=> false
p metrics[2] == metrics[3] #=> true

From the above code, we see that the FontMetrics object gives the appearance of having data for each and every character in the string, but checking the object_id for each proves that only one object per unique character has been created. While I suppose we could call this a Flyweight, I think that in Ruby we’d just say that we were caching the metrics data using a hash with an initializer. But perhaps using this vocabulary wouldn’t hurt, if we want to keep our minds focused on the high level concepts.

Reflections

After writing two articles on the the topic, I’m finding myself getting sick of design patterns. But when I look back on the code I’ve shared with you, I realize that when I built these things, I never really thought consciously about what pattern they were, and it took me until the time of writing this article to put a name on them.

A major concern I have about classical patterns is that in order to see the resemblance of the code I’ve shared to the original GoF patterns, you really need to look at things sideways and squint a bit. It’d be great for me to be able to just say ‘Use a flyweight here’ and have it mean something, but if you say that to someone without a strong background in Ruby, you may end up with hundreds of lines of a Java-esque monstrosity.

To be sure, I’m not saying that this exploration has not been worthwhile. Forcing myself to think of how many of the classic GoF patterns might materialize themselves in Ruby has been a very interesting experience, because it’s really making me think about how we build and design our code. But the problem of whether we can actually have a common vocabulary about concepts that really get distorted in translation makes me wonder about the merits of stacking up pattern definitions, at least without giving them new names.

I’m quite curious about what folks have been thinking as they read through the last two articles, in particular, I wonder if seeing me try to attack patterns from a purely pragmatic perspective has changed anything about the way readers look at these concepts. I’m also kind of curious if ‘that guy’ is out there, silently thinking to himself “Well… actually” after seeing my somewhat liberal interpretation of these classic patterns. Please leave a comment letting me know what you think!

NOTE (2011-12-27): If you want to keep receiving great content like this on a regular basis, please sign up for the Practicing Ruby Journal. It is like the original Practicing Ruby newsletter but much more polished.

blog comments powered by Disqus