Design
Patterns

What is a pattern?

"a regular and intelligible form or sequence discernible in the way in which something happens or is done"

a bit of history...

by Christopher Alexander, Sara Ishikawa and Murray Silverstein (1977)

Patterns describe a problem and then offer a solution.

The
Keystone
Pattern

Problem:

We need an arch to bear weight 

Solution:

Use a keystone

Ok, what's this got to do with software?

"The Gang of Four"

(1995)

2 key ideas

Program to an interface, not an implementation

Favour

object composition 

over

class inheritance

23 classic patterns

Creational

Abstract Factory

Builder

Factory Method

Prototype

Singleton

Structural

Adapter

Bridge

Composite

Decorator

Facade

Flyweight

Proxy

Behavioural

Chain of responsibility

Command

Interpreter

Iterator

Mediator

Memento

Observer

State

Strategy

Template

Visitor

Why learn about patterns?

Patterns form a common language

Patterns allow you to benefit from the wisdom of many previous designers

 

Don't reinvent the wheel

The
Adapter

Pattern

Problem:

Classes with incompatible interfaces

Solution:

Wrap and convert the interface

class SquarePeg
  attr_reader :width
  def initialize(width)
    @width = width
  end
end

class RoundPeg
  attr_reader :radius
  def initialize(radius)
    @radius = radius
  end
end

class RoundHole
  attr_reader :radius
  def initialize(radius)
    @radius = radius
  end

  def peg_fits?(peg)
    peg.radius <= radius
  end
end
round_hole = RoundHole.new(10)

round_peg = RoundPeg.new(8)
round_hole.peg_fits?(round_peg)
# => true

square_peg = SquarePeg.new(8)
round_hole.peg_fits?(square_peg)
# => NoMethodError: undefined method `radius' for #<SquarePeg:0x007ff9f108fb68 @width=8>
class SquarePegAdaptor
  def initialize(square_peg)
    @peg = square_peg
  end

  def radius
    Math.sqrt(((@peg.width / 2) ** 2) * 2)
  end
end
round_hole = RoundHole.new(4.0)
4.upto(7) do |i|
  peg = SquarePegAdaptor.new(SquarePeg.new(i))
  puts "Square peg of size #{i} fits: #{round_hole.peg_fits?(peg)}"
end
# Square peg of size 4 fits: true
# Square peg of size 5 fits: true
# Square peg of size 6 fits: false
# Square peg of size 7 fits: false

The

Facade
Pattern

Problem:

Very complex implementation detail 

Solution:

Create a facade with a simplified interface 

require 'net/http'
require 'uri'

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

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

puts res.body
require "open-uri"

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

DSLs are a great example of facades

When should I use a design pattern?

Design patterns are a tool, not a one size fits all solution

Don't be a pattern abuser!

But, do try to identify when they would be useful 

You've been using design patterns without even knowing it!

DSLs (Sinatra, RSpec) - Facade pattern

Ruby symbols - Flyweight pattern

Rack middleware - Decorator pattern

jQuery - Observer pattern

ActiveRecord - Adapter pattern

ActiveRecord - ActiveRecord pattern

You don't need to know all the patterns

Just be aware of them

Some quotes:

Computer Science is a science of abstraction - creating the right model for a problem and devising the appropriate mechanizable techniques to solve it."

 

A. Aho and J. Ullman, 1992

"Programs must be written for people to read, and only incidentally for machines to execute."

 

H. Abelson and G. Sussman, 1985

Further reading: Google

Just kidding.

Design Patterns in Ruby

by Russ Olsen

Patterns of Enterprise Architecture

by Martin Fowler

Learning JavaScript Design Patterns

by Addy Osmani

with huge thanks to Henry Garner for open sourcing his original presentation

Made with Slides.com