TechEd 2008 notes: “Design Patterns” in Dynamic Languages

These notes are probably fairly coherent. They’re definitely worth the read if you’re interested in design patterns and know something about Ruby, although I don’t know whether my notes would stand up very well if you don’t already know Ruby.

“Design Patterns” in Dynamic Languages
Neal Ford
Software Architect / Meme Wrangler
ThoughtWorks

Pattern solutions from weaker languages have more elegant solutions in dynamic languages

Ruby on Rails is running on IronRuby (as of RailsConf last week)

“Design Patterns: Making C++ Suck Less” by GoF

  • Really good for two things
    • Good definition for common kinds of problems we encounter
    • Insomnia cure
  • Examples in C++ and Smalltalk
  • People mistakenly thought it was a recipe book

Patterns define common problems. Dynamic languages give you better tools to solve those problems.

Not “static vs. dynamic”. It’s “ceremony vs. essence”. Statically-typed languages like C# require a lot of ceremony to get things done; long distance between intent and result. Dynamic languages have less distance.

ITERATOR

Side note: Neal isn’t a big fan of UML, because it’s not technical enough for technical people, and too technical for non-technical people. This is the only talk where you’ll ever see him use UML.

Provide way to access elements of an aggregate sequentially without exposing the details.

container.each {|i| puts i}

Built-in iterator: each. Can also make an iterator object. C# has very similar things: IEnumerable/IEnumerator and foreach

COMMAND

Encapsulates a request as an object.

commands = []
(1..10).each do |i|
  commands << proc { count += i }
end

Any language with closures already has Command built-in. There's a trend here: languages are adding built-in patterns. C# has this too.

But: Command pattern may also support undoable operations.

class Command
  def initialize(do_proc, undo_proc)

Dynamic languages don't preclude structure, but they let you delay it until necessary.

See blog post / rant: "Execution in the Kingdom of Nouns". Command was essentially designed to give you a way to pass verbs around.

BUILDER

Separate construction process so the same process can create different representations.

Dynamicize with ad hoc combinations: method_missing. So instead of calling several methods in a row to set up different features, you can do things like parsing the method name: add_cd_and_dvd_and_turbo ("And" is a "bubble word" in DSL terms: it's there for readability only.)

Used by Rails for find methods: find_by_column1_and_column2_...

INTERPRETER

Given a language, define a grammar and interpreter. (Tough to use as a recipe!)

Admission that the language you're writing your app in isn't expressive enough to get the job done.

Greenspun's Tenth Rule:

Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.

GoF book assumes you're writing a parser, but an increasingly common solution is an internal DSL (e.g. LINQ).

recipe = Recipe.new "Spicy bread"
recipe.add 200.grams.of "Flour"
recipe.add 1.lb.of "Nutmeg"

Need to be able to add methods to numbers. No problem. Open class. More flexible than extension methods in C#, because you can change or remove methods.

class Numeric
  def gram
    self
  end
  alias_method :grams, :gram
  def pound
    self * 453.59237
  end
  alias_method :pounds, :pound
  alias_method :lb, :pound
  alias_method :lbs, :pound
  def of ingredient
    if ingredient.kind_of? String
      ingredient = Ingredient.new(ingredient)
    end
    ingredient.quantity = self
    ingredient
  end
end

Side note: I might have done of by making a to_ingredient method on both String and Ingredient, and call that. Wonder if that would actually be better or not, though it would avoid the kind_of? call.

Semi-external DSL:

ingredient "flour" has Protein=11.5, Lipid=1.45, Sugars=1.12, Calcium=20, Sodium=0

Do a couple of substitutions to change that into a legal line of Ruby code, and then do instance_eval.

Internal DSL == embedded interpreter. Meets the intent of the GoF interpreter pattern better than lex/yacc, especially since it's less prohibitive. Ruby on Rails is essentially a collection of internal DSLs.

FACTORY

Define interface for creating an object; let subclasses decide which class to instantiate.

Trivial in Ruby:

def create_from_factory(factory)
  factory.new
end

DECORATOR

Attach additional responsibilities dynamically. Flexible alternative to subclassing.

module Decorator
  def initialize(decorated)
    @decorated = decorated
  end
  def method_missing(method, *args)
    args.empty? ?
      @decorated.send(method) :
      @decorated.send(method, args)
  end
end

Whip.new(Coffee.new).cost

Or, make the decorations modules:

module Whipped
  def cost
    super + 0.2
  end
end

x = Coffee.new
x.extend Sprinkles
x.extend Whipped

Or make it even nicer:

Coffee.with Sprinkles, Whip

RECORDER

class Recorder
  def initialize
    @messages = []
  end
  def method_missing(method, *args, &block)
    @messages << [method, args, block]
  end
  def play_back_to(obj)
    @messages.each do |method, args, block|
      obj.send(method, *args, &block)
    end
  end
end

Interesting nuance: what about methods that are already defined on Recorder, e.g. to_s? Anticipated in Ruby 1.8: there's a class called BlankSlate.

ADAPTER

Convert one interface to another. Round hole / square peg.

class SquarePegAdapter
  def radius
    Math.sqrt(((@peg.width / 2) ** 2) * 2)

That's the traditional adapter: a wrapper. But in Ruby, you could solve it a different way:

class SquarePeg
  def radius
    Math.sqrt(((width / 2) ** 2) * 2)
  end
end

Can also add methods to a single instance instead of the entire class.

What if SquarePeg already had a radius method?

class SquarePeg
  include InterfaceSwitching
  def width
    @width
  end
  def_interface :normal, :width

  def width
    @width / 3
  end
  def_interface :holes, :width

  def initialize(width)
    set_interface :normal
    @width = width
  end
end

Then you can use a with_interface method to adapt the class to a particular interface, yield self, then put itself back. Short-lived adapter.

DYNAMIC LANGUAGE PATTERNS

ARIDIFIER

Pragmatic Programmer: "Don't Repeat Yourself."

Ceremonious languages create floods. They create repetition you don't even see because you're so used to it. Essence languages allow aridification.

class Grade
  class << self
    def for_score_of(grade)
      case grade
        when 90..100: 'A'
        when 80..90 : 'B'
        when 70..80 : 'C'
        when 60..70 : 'D'
        when Integer: 'F'
        when /['A-D]/, /[F]/ : grade
        else raise "Not a grade: #{grade{"
      end
    end
  end
end

def test_numerical_grades
  for g in 90..100
    assert_equal "A", Grade.for_score_of(g)
  end
  for g in 80..90
    assert_equal "B", Grade.for_score_of(g)
  end

Lots of repetition. Better:

TestCrades.class_eval do
  grade_range = {
    'A' => 90..100,
    ...

  grade_range.each do |k, v|
    method_name = ("test_" + k + "_letter_grade").to_sym
    define_method method_name do
      ...

No duplication. You write the loop once, inside that call to define_method.

STATE

Door with states: Open and Closed. Model the states as mixins, and extend them. Problem: old methods don't go away.

Solution: Mixology Ruby gem that adds unmix and mixin methods.

Dynamic languages use language facilities to create simpler solution. GoF solutions are all structural; that's why they all have UML diagrams: the way to solve problems is to add structure. You can still do that in dynamic languages, but often don't need to.

Understand patterns for what they are: descriptions of common problems. Don't get caught up in implementation details. Implement solutions that are more elegant and take advantage of your tools.

"Simplexity": taking complicated code and move it to another place; may add more complexity somewhere else, but simplifies other code. Languages with strong metaprogramming.

Q&A

Which of these patterns are implementable in IronRuby? -- They're running IronRuby against the Ruby language specs. Virtually all of these should run now.

Looks like cleverness for cleverness' sake. -- Compare to AOP or LINQ. Not everyone on your team has to understand how it works internally. But don't go hog-wild with this stuff. Metaprogramming was 3.5 - 5% of his team's Ruby code. Use metaprogramming surgically, not as a sledgehammer.

Some programmers don't understand e.g. C++ templates. Is metaprogramming the same kind of problem? -- No, because in C++, templates are in your face all the time; the complexity is hard to hide. In Ruby, it just looks like you're adding keywords to the language.

Why not just write your own language on the DLR? -- Some people probably will, but writing a good, expressive language is really hard, way harder than making a good framework.

At a company that doesn't use any dynamic languages, how to decide which one to use? -- Start using one to do something you already need to do; you'll get a flavor for how it feels and how its concepts map to your view of the world. Start with infrastructure stuff. Start using rake, or writing tests with IronRuby, or do scripting.

Aren't these languages dangerous because you don't have static typing? -- Yes. But: a compiler is a good verifier, but only for a very small surface area. Unit testing, with dynamic languages, is not optional. (Arguably, it's not optional with static languages either.)

Can you create unit tests that cover all possible scenarios? -- Sure. They go for 100% code coverage.

Does he know whether RSpec works in IronRuby? -- Doesn't know, but if Rails works, RSpec likely does.

Leave a Reply

Your email address will not be published. Required fields are marked *